Expect(readErr).ToNot(HaveOccurred()) Expect(httpBody).To(Equal([]byte(`{"value":"expected value"}`))) }) Context("when incorrect http method is used", func() { It("returns a 404", func() { httpResponse, err := httpClient.Get(serverURL + "/agent") Expect(err).ToNot(HaveOccurred()) Expect(httpResponse.StatusCode).To(Equal(404)) }) }) }) Describe("GET /blobs", func() { It("returns data from file system", func() { fs.WriteFileString("/var/vcap/micro_bosh/data/cache/123-456-789", "Some data") httpResponse, err := httpClient.Get(serverURL + "/blobs/a5/123-456-789") for err != nil { httpResponse, err = httpClient.Get(serverURL + "/blobs/a5/123-456-789") } defer httpResponse.Body.Close() httpBody, readErr := ioutil.ReadAll(httpResponse.Body) Expect(readErr).ToNot(HaveOccurred()) Expect(httpResponse.StatusCode).To(Equal(200)) Expect(httpBody).To(Equal([]byte("Some data"))) }) It("closes the underlying file", func() {
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 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("sets root and vcap passwords", func() { settingsService.Settings.Env.Bosh.Password = "******" 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 set password if not provided", func() { 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 ) 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"}, } 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) ubuntuNetManager := boshnet.NewUbuntuNetManager(fs, runner, ipResolver, interfaceConfigurationCreator, arping, logger) ubuntuCertManager := boshcert.NewUbuntuCertManager(fs, runner, 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) platform = boshplatform.NewLinuxPlatform( fs, runner, sigarCollector, compressor, copier, dirProvider, vitalsService, linuxCdutil, diskManager, ubuntuNetManager, ubuntuCertManager, monitRetryStrategy, devicePathResolver, 500*time.Millisecond, 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"}}) }) 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"}}) }) 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"}}) }) 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"}}) }) 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"}}) }) 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"}}) }) 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"}}) }) It("succeeds", func() { err := boot.Run() Expect(err).ToNot(HaveOccurred()) }) }) }) }) }) }
fs = fakesys.NewFakeFileSystem() logger := boshlog.NewLogger(boshlog.LevelNone) metadataService = NewFileMetadataService( "fake-metadata-file-path", "fake-userdata-file-path", "fake-settings-file-path", fs, logger, ) }) Describe("GetInstanceID", func() { Context("when metadata service file exists", func() { BeforeEach(func() { metadataContents := `{"instance-id":"fake-instance-id"}` fs.WriteFileString("fake-metadata-file-path", metadataContents) }) It("returns instance id", func() { instanceID, err := metadataService.GetInstanceID() Expect(err).NotTo(HaveOccurred()) Expect(instanceID).To(Equal("fake-instance-id")) }) }) Context("when metadata service file does not exist", func() { It("returns an error", func() { _, err := metadataService.GetInstanceID() Expect(err).To(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)) }) }) }
failureHandler := func(alert boshalert.MonitAlert) (err error) { didHandleAlert = true return } go monit.MonitorJobFailures(failureHandler) err := doJobFailureEmail(`fake-other-email`, jobFailuresServerPort) Expect(err).ToNot(HaveOccurred()) Expect(didHandleAlert).To(BeFalse()) }) }) Describe("AddJob", func() { BeforeEach(func() { fs.WriteFileString("/some/config/path", "fake-config") }) Context("when reading configuration from config path succeeds", func() { Context("when writing job configuration succeeds", func() { It("returns no error because monit can track added job in jobs directory", func() { err := monit.AddJob("router", 0, "/some/config/path") Expect(err).ToNot(HaveOccurred()) writtenConfig, err := fs.ReadFileString( dirProvider.MonitJobsDir() + "/0000_router.monitrc") Expect(err).ToNot(HaveOccurred()) Expect(writtenConfig).To(Equal("fake-config")) }) })
func init() { Describe("concreteCompiler", func() { var ( compiler Compiler compressor *fakecmd.FakeCompressor blobstore *fakeblobstore.FakeBlobstore fs *fakesys.FakeFileSystem runner *fakecmdrunner.FakeFileLoggingCmdRunner packageApplier *fakepackages.FakeApplier packagesBc *fakebc.FakeBundleCollection ) BeforeEach(func() { compressor = fakecmd.NewFakeCompressor() blobstore = &fakeblobstore.FakeBlobstore{} fs = fakesys.NewFakeFileSystem() runner = fakecmdrunner.NewFakeFileLoggingCmdRunner() packageApplier = fakepackages.NewFakeApplier() packagesBc = fakebc.NewFakeBundleCollection() compiler = NewConcreteCompiler( compressor, blobstore, fs, runner, FakeCompileDirProvider{Dir: "/fake-compile-dir"}, packageApplier, packagesBc, ) }) BeforeEach(func() { fs.MkdirAll("/fake-compile-dir", os.ModePerm) }) Describe("Compile", func() { var ( bundle *fakebc.FakeBundle pkg Package pkgDeps []boshmodels.Package ) BeforeEach(func() { bundle = packagesBc.FakeGet(boshmodels.Package{ Name: "pkg_name", Version: "pkg_version", }) bundle.InstallPath = "/fake-dir/data/packages/pkg_name/pkg_version" bundle.EnablePath = "/fake-dir/packages/pkg_name" compressor.CompressFilesInDirTarballPath = "/tmp/compressed-compiled-package" pkg, pkgDeps = getCompileArgs() }) It("returns blob id and sha1 of created compiled package", func() { blobstore.CreateBlobID = "fake-blob-id" blobstore.CreateFingerprint = "fake-blob-sha1" blobID, sha1, err := compiler.Compile(pkg, pkgDeps) Expect(err).ToNot(HaveOccurred()) Expect(blobID).To(Equal("fake-blob-id")) Expect(sha1).To(Equal("fake-blob-sha1")) }) It("cleans up all packages before and after applying dependent packages", func() { _, _, err := compiler.Compile(pkg, pkgDeps) Expect(err).ToNot(HaveOccurred()) Expect(packageApplier.ActionsCalled).To(Equal([]string{"KeepOnly", "Apply", "Apply", "KeepOnly"})) Expect(packageApplier.KeptOnlyPackages).To(BeEmpty()) }) It("returns an error if cleaning up packages fails", func() { packageApplier.KeepOnlyErr = errors.New("fake-keep-only-error") _, _, err := compiler.Compile(pkg, pkgDeps) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-keep-only-error")) }) It("fetches source package from blobstore without checking SHA1 by default because of Director bug", func() { _, _, err := compiler.Compile(pkg, pkgDeps) Expect(err).ToNot(HaveOccurred()) Expect(blobstore.GetBlobIDs[0]).To(Equal("blobstore_id")) Expect(blobstore.GetFingerprints[0]).To(Equal("")) }) PIt("(Pending Tracker Story: <https://www.pivotaltracker.com/story/show/94524232>) fetches source package from blobstore and checks SHA1 by default in future", func() { _, _, err := compiler.Compile(pkg, pkgDeps) Expect(err).ToNot(HaveOccurred()) Expect(blobstore.GetBlobIDs[0]).To(Equal("blobstore_id")) Expect(blobstore.GetFingerprints[0]).To(Equal("sha1")) }) It("returns an error if removing compile target directory during uncompression fails", func() { fs.RegisterRemoveAllError("/fake-compile-dir/pkg_name", errors.New("fake-remove-error")) _, _, err := compiler.Compile(pkg, pkgDeps) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-remove-error")) }) It("returns an error if creating compile target directory during uncompression fails", func() { fs.RegisterMkdirAllError("/fake-compile-dir/pkg_name", errors.New("fake-mkdir-error")) _, _, err := compiler.Compile(pkg, pkgDeps) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-mkdir-error")) }) It("returns an error if removing temporary compile target directory during uncompression fails", func() { fs.RegisterRemoveAllError("/fake-compile-dir/pkg_name-bosh-agent-unpack", errors.New("fake-remove-error")) _, _, err := compiler.Compile(pkg, pkgDeps) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-remove-error")) }) It("returns an error if creating temporary compile target directory during uncompression fails", func() { fs.RegisterMkdirAllError("/fake-compile-dir/pkg_name-bosh-agent-unpack", errors.New("fake-mkdir-error")) _, _, err := compiler.Compile(pkg, pkgDeps) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-mkdir-error")) }) It("returns an error if target directory is empty during uncompression", func() { pkg.BlobstoreID = "" _, _, err := compiler.Compile(pkg, pkgDeps) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Blobstore ID for package '%s' is empty", pkg.Name)) }) It("installs dependent packages", func() { _, _, err := compiler.Compile(pkg, pkgDeps) Expect(err).ToNot(HaveOccurred()) Expect(packageApplier.AppliedPackages).To(Equal(pkgDeps)) }) It("cleans up the compile directory", func() { _, _, err := compiler.Compile(pkg, pkgDeps) Expect(err).ToNot(HaveOccurred()) Expect(fs.FileExists("/fake-compile-dir/pkg_name")).To(BeFalse()) }) It("installs, enables and later cleans up bundle", func() { _, _, err := compiler.Compile(pkg, pkgDeps) Expect(err).ToNot(HaveOccurred()) Expect(bundle.ActionsCalled).To(Equal([]string{ "InstallWithoutContents", "Enable", "Disable", "Uninstall", })) }) Context("when packaging script exists", func() { BeforeEach(func() { compressor.DecompressFileToDirCallBack = func() { fs.WriteFileString("/fake-compile-dir/pkg_name/packaging", "hi") } }) It("runs packaging script ", func() { _, _, err := compiler.Compile(pkg, pkgDeps) Expect(err).ToNot(HaveOccurred()) expectedCmd := boshsys.Command{ Name: "bash", Args: []string{"-x", "packaging"}, Env: map[string]string{ "BOSH_COMPILE_TARGET": "/fake-compile-dir/pkg_name", "BOSH_INSTALL_TARGET": "/fake-dir/packages/pkg_name", "BOSH_PACKAGE_NAME": "pkg_name", "BOSH_PACKAGE_VERSION": "pkg_version", }, WorkingDir: "/fake-compile-dir/pkg_name", } Expect(len(runner.RunCommands)).To(Equal(1)) Expect(runner.RunCommands[0]).To(Equal(expectedCmd)) Expect(runner.RunCommandJobName).To(Equal("compilation")) Expect(runner.RunCommandTaskName).To(Equal("packaging")) }) It("propagates the error from packaging script", func() { runner.RunCommandErr = errors.New("fake-packaging-error") _, _, err := compiler.Compile(pkg, pkgDeps) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-packaging-error")) }) }) It("does not run packaging script when script does not exist", func() { _, _, err := compiler.Compile(pkg, pkgDeps) Expect(err).ToNot(HaveOccurred()) Expect(runner.RunCommands).To(BeEmpty()) }) It("compresses compiled package", func() { _, _, err := compiler.Compile(pkg, pkgDeps) Expect(err).ToNot(HaveOccurred()) // archive was downloaded from the blobstore and decompress to this temp dir Expect(compressor.DecompressFileToDirDirs[0]).To(Equal("/fake-compile-dir/pkg_name-bosh-agent-unpack")) Expect(compressor.DecompressFileToDirTarballPaths[0]).To(Equal(blobstore.GetFileName)) // contents were moved from the temp dir to the install/enable dir Expect(fs.RenameOldPaths[0]).To(Equal("/fake-compile-dir/pkg_name-bosh-agent-unpack")) Expect(fs.RenameNewPaths[0]).To(Equal("/fake-compile-dir/pkg_name")) // install path, presumably with your packaged code, was compressed installPath := "/fake-dir/data/packages/pkg_name/pkg_version" Expect(compressor.CompressFilesInDirDir).To(Equal(installPath)) }) It("uploads compressed package to blobstore", func() { compressor.CompressFilesInDirTarballPath = "/tmp/compressed-compiled-package" _, _, err := compiler.Compile(pkg, pkgDeps) Expect(err).ToNot(HaveOccurred()) Expect(blobstore.CreateFileNames[0]).To(Equal("/tmp/compressed-compiled-package")) }) It("returs error if uploading compressed package fails", func() { blobstore.CreateErr = errors.New("fake-create-err") _, _, err := compiler.Compile(pkg, pkgDeps) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-create-err")) }) It("cleans up compressed package after uploading it to blobstore", func() { var beforeCleanUpTarballPath, afterCleanUpTarballPath string blobstore.CreateCallBack = func() { beforeCleanUpTarballPath = compressor.CleanUpTarballPath } _, _, err := compiler.Compile(pkg, pkgDeps) Expect(err).ToNot(HaveOccurred()) // Compressed package is not cleaned up before blobstore upload Expect(beforeCleanUpTarballPath).To(Equal("")) // Deleted after it was uploaded afterCleanUpTarballPath = compressor.CleanUpTarballPath Expect(afterCleanUpTarballPath).To(Equal("/tmp/compressed-compiled-package")) }) }) }) }
Expect(err.Error()).To(ContainSubstring("missing blobstore_path")) }) It("returns error when blobstore path is not a string", func() { options := map[string]interface{}{"blobstore_path": 443} blobstore = NewLocalBlobstore(fs, uuidGen, options) err := blobstore.Validate() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("blobstore_path must be a string")) }) }) Describe("Get", func() { It("fetches the local blob contents", func() { fs.WriteFileString(fakeBlobstorePath+"/fake-blob-id", "fake contents") tempFile, err := fs.TempFile("bosh-blobstore-local-TestLocalGet") Expect(err).ToNot(HaveOccurred()) fs.ReturnTempFile = tempFile defer fs.RemoveAll(tempFile.Name()) _, err = blobstore.Get("fake-blob-id", "") Expect(err).ToNot(HaveOccurred()) fileStats := fs.GetFileTestStat(tempFile.Name()) Expect(fileStats).ToNot(BeNil()) Expect("fake contents").To(Equal(fileStats.StringContents())) })
func describeUbuntuNetManager() { var ( fs *fakesys.FakeFileSystem cmdRunner *fakesys.FakeCmdRunner ipResolver *fakeip.FakeResolver addressBroadcaster *fakearp.FakeAddressBroadcaster netManager UbuntuNetManager interfaceConfigurationCreator InterfaceConfigurationCreator ) 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 } stubInterfacesWithVirtual := func(physicalInterfaces map[string]boshsettings.Network, virtualInterfaces []string) { interfacePaths := []string{} for iface, networkSettings := range physicalInterfaces { interfacePaths = append(interfacePaths, writeNetworkDevice(iface, networkSettings.Mac, true)) } for _, iface := range virtualInterfaces { interfacePaths = append(interfacePaths, writeNetworkDevice(iface, "virtual", false)) } fs.SetGlob("/sys/class/net/*", interfacePaths) } stubInterfaces := func(physicalInterfaces map[string]boshsettings.Network) { stubInterfacesWithVirtual(physicalInterfaces, nil) } BeforeEach(func() { fs = fakesys.NewFakeFileSystem() cmdRunner = fakesys.NewFakeCmdRunner() ipResolver = &fakeip.FakeResolver{} logger := boshlog.NewLogger(boshlog.LevelNone) interfaceConfigurationCreator = NewInterfaceConfigurationCreator(logger) addressBroadcaster = &fakearp.FakeAddressBroadcaster{} netManager = NewUbuntuNetManager( fs, cmdRunner, ipResolver, interfaceConfigurationCreator, addressBroadcaster, logger, ).(UbuntuNetManager) }) Describe("ComputeNetworkConfig", func() { Context("when there is one manual network and neither is marked as default for DNS", func() { It("should use the manual network for DNS", func() { networks := boshsettings.Networks{ "manual": factory.Network{DNS: &[]string{"8.8.8.8"}}.Build(), } stubInterfaces(networks) _, _, dnsServers, err := netManager.ComputeNetworkConfig(networks) Expect(err).ToNot(HaveOccurred()) Expect(dnsServers).To(Equal([]string{"8.8.8.8"})) }) }) Context("when there is a vip network and a manual network and neither is marked as default for DNS", func() { It("should use the manual network for DNS", func() { networks := boshsettings.Networks{ "vip": boshsettings.Network{Type: "vip"}, "manual": factory.Network{Type: "manual", DNS: &[]string{"8.8.8.8"}}.Build(), } stubInterfaces(networks) _, _, dnsServers, err := netManager.ComputeNetworkConfig(networks) Expect(err).ToNot(HaveOccurred()) Expect(dnsServers).To(Equal([]string{"8.8.8.8"})) }) }) Context("when there is a vip network and a manual network and the manual network is marked as default for DNS", func() { It("should use the manual network for DNS", func() { networks := boshsettings.Networks{ "vip": boshsettings.Network{Type: "vip"}, "manual": factory.Network{Type: "manual", DNS: &[]string{"8.8.8.8"}, Default: []string{"dns"}}.Build(), } stubInterfaces(networks) _, _, dnsServers, err := netManager.ComputeNetworkConfig(networks) Expect(err).ToNot(HaveOccurred()) Expect(dnsServers).To(Equal([]string{"8.8.8.8"})) }) }) Context("when specified more than one DNS", func() { It("extracts all DNS servers from the network configured as default DNS", func() { networks := boshsettings.Networks{ "default": factory.Network{ IP: "10.10.0.32", Netmask: "255.255.255.0", Mac: "aa::bb::cc", Default: []string{"dns", "gateway"}, DNS: &[]string{"54.209.78.6", "127.0.0.5"}, Gateway: "10.10.0.1", }.Build(), } stubInterfaces(networks) staticInterfaceConfigurations, dhcpInterfaceConfigurations, dnsServers, err := netManager.ComputeNetworkConfig(networks) Expect(err).ToNot(HaveOccurred()) Expect(staticInterfaceConfigurations).To(Equal([]StaticInterfaceConfiguration{ { Name: "default", Address: "10.10.0.32", Netmask: "255.255.255.0", Network: "10.10.0.0", Broadcast: "10.10.0.255", Mac: "aa::bb::cc", Gateway: "10.10.0.1", }, })) Expect(dhcpInterfaceConfigurations).To(BeEmpty()) Expect(dnsServers).To(Equal([]string{"54.209.78.6", "127.0.0.5"})) }) }) }) Describe("SetupNetworking", func() { var ( dhcpNetwork boshsettings.Network staticNetwork boshsettings.Network expectedNetworkConfigurationForStaticAndDhcp string ) BeforeEach(func() { dhcpNetwork = boshsettings.Network{ Type: "dynamic", Default: []string{"dns"}, DNS: []string{"8.8.8.8", "9.9.9.9"}, Mac: "fake-dhcp-mac-address", } staticNetwork = boshsettings.Network{ Type: "manual", IP: "1.2.3.4", Netmask: "255.255.255.0", Gateway: "3.4.5.6", Mac: "fake-static-mac-address", } expectedNetworkConfigurationForStaticAndDhcp = `# Generated by bosh-agent auto lo iface lo inet loopback auto ethdhcp iface ethdhcp inet dhcp auto ethstatic iface ethstatic inet static address 1.2.3.4 network 1.2.3.0 netmask 255.255.255.0 broadcast 1.2.3.255 gateway 3.4.5.6 dns-nameservers 8.8.8.8 9.9.9.9` }) It("writes interfaces in /etc/network/interfaces in alphabetic order", func() { anotherDHCPNetwork := boshsettings.Network{ Type: "dynamic", Default: []string{"dns"}, DNS: []string{"8.8.8.8", "9.9.9.9"}, Mac: "fake-another-mac-address", } stubInterfaces(map[string]boshsettings.Network{ "ethstatic": staticNetwork, "ethdhcp1": dhcpNetwork, "ethdhcp0": anotherDHCPNetwork, }) err := netManager.SetupNetworking(boshsettings.Networks{ "dhcp-network-1": dhcpNetwork, "dhcp-network-2": anotherDHCPNetwork, "static-network": staticNetwork, }, nil) Expect(err).ToNot(HaveOccurred()) networkConfig := fs.GetFileTestStat("/etc/network/interfaces") Expect(networkConfig).ToNot(BeNil()) expectedNetworkConfigurationForStaticAndDhcp = `# Generated by bosh-agent auto lo iface lo inet loopback auto ethdhcp0 iface ethdhcp0 inet dhcp auto ethdhcp1 iface ethdhcp1 inet dhcp auto ethstatic iface ethstatic inet static address 1.2.3.4 network 1.2.3.0 netmask 255.255.255.0 broadcast 1.2.3.255 gateway 3.4.5.6 dns-nameservers 8.8.8.8 9.9.9.9` Expect(networkConfig.StringContents()).To(Equal(expectedNetworkConfigurationForStaticAndDhcp)) }) It("writes /etc/network/interfaces without dns-namservers if there are no dns servers", func() { staticNetworkWithoutDNS := boshsettings.Network{ Type: "manual", IP: "1.2.3.4", Netmask: "255.255.255.0", Gateway: "3.4.5.6", Mac: "fake-static-mac-address", } stubInterfaces(map[string]boshsettings.Network{ "ethstatic": staticNetworkWithoutDNS, }) err := netManager.SetupNetworking(boshsettings.Networks{"static-network": staticNetworkWithoutDNS}, nil) Expect(err).ToNot(HaveOccurred()) networkConfig := fs.GetFileTestStat("/etc/network/interfaces") Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(`# Generated by bosh-agent auto lo iface lo inet loopback auto ethstatic iface ethstatic inet static address 1.2.3.4 network 1.2.3.0 netmask 255.255.255.0 broadcast 1.2.3.255 gateway 3.4.5.6 `)) }) It("returns errors from glob /sys/class/net/", func() { fs.GlobErr = errors.New("fs-glob-error") err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fs-glob-error")) }) It("returns errors from writing the network configuration", func() { stubInterfaces(map[string]boshsettings.Network{ "dhcp": dhcpNetwork, "static": staticNetwork, }) fs.WriteFileError = errors.New("fs-write-file-error") err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fs-write-file-error")) }) It("returns errors when it can't creating network interface configurations", func() { stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) staticNetwork.Netmask = "not an ip" //will cause InterfaceConfigurationCreator to fail err := netManager.SetupNetworking(boshsettings.Networks{"static-network": staticNetwork}, nil) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Creating interface configurations")) }) It("writes a dhcp configuration if there are dhcp networks", func() { stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).ToNot(HaveOccurred()) dhcpConfig := fs.GetFileTestStat("/etc/dhcp/dhclient.conf") Expect(dhcpConfig).ToNot(BeNil()) Expect(dhcpConfig.StringContents()).To(Equal(`# Generated by bosh-agent option rfc3442-classless-static-routes code 121 = array of unsigned integer 8; send host-name "<hostname>"; request subnet-mask, broadcast-address, time-offset, routers, domain-name, domain-name-servers, domain-search, host-name, netbios-name-servers, netbios-scope, interface-mtu, rfc3442-classless-static-routes, ntp-servers; prepend domain-name-servers 8.8.8.8, 9.9.9.9; `)) }) It("writes a dhcp configuration without prepended dns servers if there are no dns servers specified", func() { dhcpNetworkWithoutDNS := boshsettings.Network{ Type: "dynamic", Mac: "fake-dhcp-mac-address", } stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, }) err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetworkWithoutDNS}, nil) Expect(err).ToNot(HaveOccurred()) dhcpConfig := fs.GetFileTestStat("/etc/dhcp/dhclient.conf") Expect(dhcpConfig).ToNot(BeNil()) Expect(dhcpConfig.StringContents()).To(Equal(`# Generated by bosh-agent option rfc3442-classless-static-routes code 121 = array of unsigned integer 8; send host-name "<hostname>"; request subnet-mask, broadcast-address, time-offset, routers, domain-name, domain-name-servers, domain-search, host-name, netbios-name-servers, netbios-scope, interface-mtu, rfc3442-classless-static-routes, ntp-servers; `)) }) It("returns an error if it can't write a dhcp configuration", func() { stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) fs.WriteFileErrors["/etc/dhcp/dhclient.conf"] = errors.New("dhclient.conf-write-error") err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("dhclient.conf-write-error")) }) It("doesn't write a dhcp configuration if there are no dhcp networks", func() { stubInterfaces(map[string]boshsettings.Network{ "ethstatic": staticNetwork, }) err := netManager.SetupNetworking(boshsettings.Networks{"static-network": staticNetwork}, nil) Expect(err).ToNot(HaveOccurred()) dhcpConfig := fs.GetFileTestStat("/etc/dhcp/dhclient.conf") Expect(dhcpConfig).To(BeNil()) }) It("restarts the networks if /etc/network/interfaces changes", func() { initialDhcpConfig := `# Generated by bosh-agent option rfc3442-classless-static-routes code 121 = array of unsigned integer 8; send host-name "<hostname>"; request subnet-mask, broadcast-address, time-offset, routers, domain-name, domain-name-servers, domain-search, host-name, netbios-name-servers, netbios-scope, interface-mtu, rfc3442-classless-static-routes, ntp-servers; prepend domain-name-servers 8.8.8.8, 9.9.9.9; ` stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) fs.WriteFileString("/etc/dhcp/dhclient.conf", initialDhcpConfig) err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).ToNot(HaveOccurred()) Expect(len(cmdRunner.RunCommands)).To(Equal(5)) Expect(cmdRunner.RunCommands[0]).To(Equal([]string{"pkill", "dhclient"})) Expect(cmdRunner.RunCommands[1:3]).To(ContainElement([]string{"resolvconf", "-d", "ethdhcp.dhclient"})) Expect(cmdRunner.RunCommands[1:3]).To(ContainElement([]string{"resolvconf", "-d", "ethstatic.dhclient"})) Expect(cmdRunner.RunCommands[3]).To(Equal([]string{"ifdown", "-a", "--no-loopback"})) Expect(cmdRunner.RunCommands[4]).To(Equal([]string{"ifup", "-a", "--no-loopback"})) }) It("doesn't restart the networks if /etc/network/interfaces and /etc/dhcp/dhclient.conf don't change", func() { initialDhcpConfig := `# Generated by bosh-agent option rfc3442-classless-static-routes code 121 = array of unsigned integer 8; send host-name "<hostname>"; request subnet-mask, broadcast-address, time-offset, routers, domain-name, domain-name-servers, domain-search, host-name, netbios-name-servers, netbios-scope, interface-mtu, rfc3442-classless-static-routes, ntp-servers; prepend domain-name-servers 8.8.8.8, 9.9.9.9; ` stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) fs.WriteFileString("/etc/network/interfaces", expectedNetworkConfigurationForStaticAndDhcp) fs.WriteFileString("/etc/dhcp/dhclient.conf", initialDhcpConfig) err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).ToNot(HaveOccurred()) networkConfig := fs.GetFileTestStat("/etc/network/interfaces") Expect(networkConfig.StringContents()).To(Equal(expectedNetworkConfigurationForStaticAndDhcp)) dhcpConfig := fs.GetFileTestStat("/etc/dhcp/dhclient.conf") Expect(dhcpConfig.StringContents()).To(Equal(initialDhcpConfig)) Expect(len(cmdRunner.RunCommands)).To(Equal(0)) }) It("restarts the networks if /etc/dhcp/dhclient.conf changes", func() { stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) fs.WriteFileString("/etc/network/interfaces", expectedNetworkConfigurationForStaticAndDhcp) err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).ToNot(HaveOccurred()) networkConfig := fs.GetFileTestStat("/etc/network/interfaces") Expect(networkConfig.StringContents()).To(Equal(expectedNetworkConfigurationForStaticAndDhcp)) Expect(len(cmdRunner.RunCommands)).To(Equal(5)) Expect(cmdRunner.RunCommands[0]).To(Equal([]string{"pkill", "dhclient"})) Expect(cmdRunner.RunCommands[1:3]).To(ContainElement([]string{"resolvconf", "-d", "ethdhcp.dhclient"})) Expect(cmdRunner.RunCommands[1:3]).To(ContainElement([]string{"resolvconf", "-d", "ethstatic.dhclient"})) Expect(cmdRunner.RunCommands[3]).To(Equal([]string{"ifdown", "-a", "--no-loopback"})) Expect(cmdRunner.RunCommands[4]).To(Equal([]string{"ifup", "-a", "--no-loopback"})) }) It("broadcasts MAC addresses for all interfaces", func() { stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) errCh := make(chan error) err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, errCh) Expect(err).ToNot(HaveOccurred()) broadcastErr := <-errCh // wait for all arpings Expect(broadcastErr).ToNot(HaveOccurred()) Expect(addressBroadcaster.BroadcastMACAddressesAddresses).To(Equal([]boship.InterfaceAddress{ boship.NewSimpleInterfaceAddress("ethstatic", "1.2.3.4"), boship.NewResolvingInterfaceAddress("ethdhcp", ipResolver), })) }) It("skips vip networks", func() { stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) vipNetwork := boshsettings.Network{ Type: "vip", Default: []string{"dns"}, DNS: []string{"8.8.8.8", "9.9.9.9"}, Mac: "fake-vip-mac-address", IP: "9.8.7.6", } err := netManager.SetupNetworking(boshsettings.Networks{ "dhcp-network": dhcpNetwork, "static-network": staticNetwork, "vip-network": vipNetwork, }, nil) Expect(err).ToNot(HaveOccurred()) networkConfig := fs.GetFileTestStat("/etc/network/interfaces") Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(expectedNetworkConfigurationForStaticAndDhcp)) }) Context("when no MAC address is provided in the settings", func() { It("configures network for single device", func() { staticNetworkWithoutMAC := boshsettings.Network{ Type: "manual", IP: "2.2.2.2", Netmask: "255.255.255.0", Gateway: "3.4.5.6", } stubInterfaces( map[string]boshsettings.Network{ "ethstatic": staticNetwork, }, ) err := netManager.SetupNetworking(boshsettings.Networks{ "static-network": staticNetworkWithoutMAC, }, nil) Expect(err).ToNot(HaveOccurred()) networkConfig := fs.GetFileTestStat("/etc/network/interfaces") Expect(networkConfig).ToNot(BeNil()) expectedNetworkConfiguration := `# Generated by bosh-agent auto lo iface lo inet loopback auto ethstatic iface ethstatic inet static address 2.2.2.2 network 2.2.2.0 netmask 255.255.255.0 broadcast 2.2.2.255 gateway 3.4.5.6 ` Expect(networkConfig.StringContents()).To(Equal(expectedNetworkConfiguration)) }) It("configures network for a single physical device, when a virtual device is also present", func() { staticNetworkWithoutMAC := boshsettings.Network{ Type: "manual", IP: "2.2.2.2", Netmask: "255.255.255.0", Gateway: "3.4.5.6", } stubInterfacesWithVirtual( map[string]boshsettings.Network{ "ethstatic": staticNetwork, }, []string{"virtual"}, ) err := netManager.SetupNetworking(boshsettings.Networks{ "static-network": staticNetworkWithoutMAC, }, nil) Expect(err).ToNot(HaveOccurred()) networkConfig := fs.GetFileTestStat("/etc/network/interfaces") Expect(networkConfig).ToNot(BeNil()) expectedNetworkConfiguration := `# Generated by bosh-agent auto lo iface lo inet loopback auto ethstatic iface ethstatic inet static address 2.2.2.2 network 2.2.2.0 netmask 255.255.255.0 broadcast 2.2.2.255 gateway 3.4.5.6 ` Expect(networkConfig.StringContents()).To(Equal(expectedNetworkConfiguration)) }) }) }) Describe("GetConfiguredNetworkInterfaces", func() { Context("when there are network devices", func() { BeforeEach(func() { interfacePaths := []string{} interfacePaths = append(interfacePaths, writeNetworkDevice("fake-eth0", "aa:bb", true)) interfacePaths = append(interfacePaths, writeNetworkDevice("fake-eth1", "cc:dd", true)) interfacePaths = append(interfacePaths, writeNetworkDevice("fake-eth2", "ee:ff", true)) fs.SetGlob("/sys/class/net/*", interfacePaths) }) It("returns networks that are defined in /etc/network/interfaces", func() { cmdRunner.AddCmdResult("ifup --no-act fake-eth0", fakesys.FakeCmdResult{ Stdout: "", Stderr: "ifup: interface fake-eth0 already configured", ExitStatus: 0, }) cmdRunner.AddCmdResult("ifup --no-act fake-eth1", fakesys.FakeCmdResult{ Stdout: "", Stderr: "Ignoring unknown interface fake-eth1=fake-eth1.", ExitStatus: 0, }) cmdRunner.AddCmdResult("ifup --no-act fake-eth2", fakesys.FakeCmdResult{ Stdout: "", Stderr: "ifup: interface fake-eth2 already configured", ExitStatus: 0, }) interfaces, err := netManager.GetConfiguredNetworkInterfaces() Expect(err).ToNot(HaveOccurred()) Expect(interfaces).To(ConsistOf("fake-eth0", "fake-eth2")) }) }) Context("when there are no network devices", func() { It("returns empty list", func() { interfaces, err := netManager.GetConfiguredNetworkInterfaces() Expect(err).ToNot(HaveOccurred()) Expect(interfaces).To(Equal([]string{})) }) }) }) }
func init() { Describe("concreteV1Service", func() { var ( fs *fakesys.FakeFileSystem specPath = "/spec.json" service V1Service ) BeforeEach(func() { fs = fakesys.NewFakeFileSystem() service = NewConcreteV1Service(fs, specPath) }) Describe("Get", func() { Context("when filesystem has a spec file", func() { BeforeEach(func() { fs.WriteFileString(specPath, `{"deployment":"fake-deployment-name"}`) }) It("reads spec from filesystem", func() { spec, err := service.Get() Expect(err).ToNot(HaveOccurred()) Expect(spec).To(Equal(V1ApplySpec{Deployment: "fake-deployment-name"})) }) It("returns error if reading spec from filesystem errs", func() { fs.ReadFileError = errors.New("fake-read-error") spec, err := service.Get() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-read-error")) Expect(spec).To(Equal(V1ApplySpec{})) }) }) Context("when filesystem does not have a spec file", func() { It("reads spec from filesystem", func() { spec, err := service.Get() Expect(err).ToNot(HaveOccurred()) Expect(spec).To(Equal(V1ApplySpec{})) }) }) }) Describe("Set", func() { newSpec := V1ApplySpec{Deployment: "fake-deployment-name"} It("writes spec to filesystem", func() { err := service.Set(newSpec) Expect(err).ToNot(HaveOccurred()) specPathStats := fs.GetFileTestStat(specPath) Expect(specPathStats).ToNot(BeNil()) boshassert.MatchesJSONBytes(GinkgoT(), newSpec, specPathStats.Content) }) It("returns error if writing spec to filesystem errs", func() { fs.WriteFileError = errors.New("fake-write-error") err := service.Set(newSpec) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-write-error")) }) }) Describe("PopulateDHCPNetworks", func() { var settings boshsettings.Settings var unresolvedSpec V1ApplySpec var staticSpec NetworkSpec var dhcpSpec NetworkSpec var manualSetting boshsettings.Network var dynamicSetting boshsettings.Network BeforeEach(func() { settings = boshsettings.Settings{ Networks: boshsettings.Networks{}, } manualSetting = boshsettings.Network{ Type: "manual", IP: "fake-manual-ip", Netmask: "fake-manual-netmask", Gateway: "fake-manual-gateway", Mac: "fake-manual-mac", } dynamicSetting = boshsettings.Network{ Type: "dynamic", IP: "fake-dynamic-ip", Netmask: "fake-dynamic-netmask", Gateway: "fake-dynamic-gateway", } unresolvedSpec = V1ApplySpec{ Deployment: "fake-deployment", NetworkSpecs: map[string]NetworkSpec{}, } staticSpec = NetworkSpec{ Fields: map[string]interface{}{ "ip": "fake-net1-ip", "netmask": "fake-net1-netmask", "gateway": "fake-net1-gateway", "mac": "fake-net1-mac", }, } dhcpSpec = NetworkSpec{ Fields: map[string]interface{}{ "type": NetworkSpecTypeDynamic, "ip": "fake-net2-ip", "netmask": "fake-net2-netmask", "gateway": "fake-net2-gateway", }, } }) Context("when associated network is in settings", func() { Context("when there are no networks configured with DHCP", func() { BeforeEach(func() { settings.Networks["fake-net"] = manualSetting unresolvedSpec.NetworkSpecs["fake-net"] = staticSpec }) It("returns spec without modifying any networks", func() { spec, err := service.PopulateDHCPNetworks(unresolvedSpec, settings) Expect(err).ToNot(HaveOccurred()) Expect(spec).To(Equal(unresolvedSpec)) }) }) Context("when there is network with name 'local' and ip 127.0.0.1", func() { BeforeEach(func() { unresolvedSpec.NetworkSpecs["local"] = NetworkSpec{ Fields: map[string]interface{}{"ip": "127.0.0.1"}, } }) It("returns spec without modifying any networks", func() { spec, err := service.PopulateDHCPNetworks(unresolvedSpec, settings) Expect(err).ToNot(HaveOccurred()) Expect(spec).To(Equal(unresolvedSpec)) }) }) Context("when there are networks configured with DHCP", func() { BeforeEach(func() { settings.Networks["static-net1"] = manualSetting settings.Networks["dhcp-net2"] = dynamicSetting unresolvedSpec.NetworkSpecs["static-net1"] = staticSpec unresolvedSpec.NetworkSpecs["dhcp-net2"] = dhcpSpec }) It("returns spec with networks modified via DHCP and keeps everything else the same", func() { spec, err := service.PopulateDHCPNetworks(unresolvedSpec, settings) Expect(err).ToNot(HaveOccurred()) Expect(spec).To(Equal(V1ApplySpec{ Deployment: "fake-deployment", NetworkSpecs: map[string]NetworkSpec{ "static-net1": staticSpec, "dhcp-net2": NetworkSpec{ Fields: map[string]interface{}{ "type": NetworkSpecTypeDynamic, "ip": dynamicSetting.IP, "netmask": dynamicSetting.Netmask, "gateway": dynamicSetting.Gateway, }, }, }, })) }) }) }) Context("when associated network cannot be found in settings", func() { BeforeEach(func() { settings.Networks["net-present-in-settings"] = manualSetting unresolvedSpec.NetworkSpecs["net-present-in-settings"] = staticSpec unresolvedSpec.NetworkSpecs["net-not-present-in-settings"] = dhcpSpec }) It("returns error", func() { spec, err := service.PopulateDHCPNetworks(unresolvedSpec, settings) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Network 'net-not-present-in-settings' is not found in settings")) Expect(spec).To(Equal(V1ApplySpec{})) }) }) }) }) }
}) Describe("GetFileContents", func() { Context("when disk path does not exist", func() { It("returns an error if diskpath does not exist", func() { _, err := diskUtil.GetFilesContents([]string{"fake-file-path-1"}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("disk path 'fake-disk-path' does not exist")) }) }) Context("when disk path does not exist", func() { BeforeEach(func() { fs.MkdirAll("fake-disk-path", 0700) fs.TempDirDir = "fake-tempdir" fs.WriteFileString("fake-tempdir/fake-file-path-1", "fake-contents-1") fs.WriteFileString("fake-tempdir/fake-file-path-2", "fake-contents-2") }) It("mounts disk path to temporary directory", func() { _, err := diskUtil.GetFilesContents([]string{"fake-file-path-1"}) Expect(err).ToNot(HaveOccurred()) Expect(mounter.MountPartitionPaths).To(ContainElement("fake-disk-path")) Expect(mounter.MountMountPoints).To(ContainElement("fake-tempdir")) }) It("returns contents of files on a disk", func() { contents, err := diskUtil.GetFilesContents([]string{"fake-file-path-1", "fake-file-path-2"}) Expect(err).ToNot(HaveOccurred()) Expect(len(contents)).To(Equal(2))
func describeCentosNetManager() { var ( fs *fakesys.FakeFileSystem cmdRunner *fakesys.FakeCmdRunner ipResolver *fakeip.FakeResolver addressBroadcaster *fakearp.FakeAddressBroadcaster netManager Manager interfaceConfigurationCreator InterfaceConfigurationCreator ) BeforeEach(func() { fs = fakesys.NewFakeFileSystem() cmdRunner = fakesys.NewFakeCmdRunner() ipResolver = &fakeip.FakeResolver{} logger := boshlog.NewLogger(boshlog.LevelNone) interfaceConfigurationCreator = NewInterfaceConfigurationCreator(logger) addressBroadcaster = &fakearp.FakeAddressBroadcaster{} netManager = NewCentosNetManager( fs, cmdRunner, ipResolver, interfaceConfigurationCreator, addressBroadcaster, logger, ) }) 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 } Describe("SetupNetworking", func() { var ( dhcpNetwork boshsettings.Network staticNetwork boshsettings.Network expectedNetworkConfigurationForStatic string expectedNetworkConfigurationForDHCP string expectedDhclientConfiguration string ) BeforeEach(func() { dhcpNetwork = boshsettings.Network{ Type: "dynamic", Default: []string{"dns"}, DNS: []string{"8.8.8.8", "9.9.9.9"}, Mac: "fake-dhcp-mac-address", } staticNetwork = boshsettings.Network{ Type: "manual", IP: "1.2.3.4", Netmask: "255.255.255.0", Gateway: "3.4.5.6", Mac: "fake-static-mac-address", } expectedNetworkConfigurationForStatic = `DEVICE=ethstatic BOOTPROTO=static IPADDR=1.2.3.4 NETMASK=255.255.255.0 BROADCAST=1.2.3.255 GATEWAY=3.4.5.6 ONBOOT=yes PEERDNS=no DNS1=8.8.8.8 DNS2=9.9.9.9 ` expectedNetworkConfigurationForDHCP = `DEVICE=ethdhcp BOOTPROTO=dhcp ONBOOT=yes PEERDNS=yes ` expectedDhclientConfiguration = `# Generated by bosh-agent option rfc3442-classless-static-routes code 121 = array of unsigned integer 8; send host-name "<hostname>"; request subnet-mask, broadcast-address, time-offset, routers, domain-name, domain-name-servers, domain-search, host-name, netbios-name-servers, netbios-scope, interface-mtu, rfc3442-classless-static-routes, ntp-servers; prepend domain-name-servers 8.8.8.8, 9.9.9.9; ` }) stubInterfacesWithVirtual := func(physicalInterfaces map[string]boshsettings.Network, virtualInterfaces []string) { interfacePaths := []string{} for iface, networkSettings := range physicalInterfaces { interfacePaths = append(interfacePaths, writeNetworkDevice(iface, networkSettings.Mac, true)) } for _, iface := range virtualInterfaces { interfacePaths = append(interfacePaths, writeNetworkDevice(iface, "virtual", false)) } fs.SetGlob("/sys/class/net/*", interfacePaths) } stubInterfaces := func(physicalInterfaces map[string]boshsettings.Network) { stubInterfacesWithVirtual(physicalInterfaces, nil) } It("writes a network script for static and dynamic interfaces", func() { stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).ToNot(HaveOccurred()) staticConfig := fs.GetFileTestStat("/etc/sysconfig/network-scripts/ifcfg-ethstatic") Expect(staticConfig).ToNot(BeNil()) Expect(staticConfig.StringContents()).To(Equal(expectedNetworkConfigurationForStatic)) dhcpConfig := fs.GetFileTestStat("/etc/sysconfig/network-scripts/ifcfg-ethdhcp") Expect(dhcpConfig).ToNot(BeNil()) Expect(dhcpConfig.StringContents()).To(Equal(expectedNetworkConfigurationForDHCP)) }) It("returns errors from glob /sys/class/net/", func() { fs.GlobErr = errors.New("fs-glob-error") err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fs-glob-error")) }) It("returns errors from writing the network configuration", func() { stubInterfaces(map[string]boshsettings.Network{ "dhcp": dhcpNetwork, "static": staticNetwork, }) fs.WriteFileError = errors.New("fs-write-file-error") err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fs-write-file-error")) }) It("returns errors when it can't create network interface configurations", func() { stubInterfaces(map[string]boshsettings.Network{ "ethstatic": staticNetwork, }) staticNetwork.Netmask = "not an ip" //will cause InterfaceConfigurationCreator to fail err := netManager.SetupNetworking(boshsettings.Networks{"static-network": staticNetwork}, nil) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Creating interface configurations")) }) It("wrtites a dhcp configuration if there are dhcp networks", func() { stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).ToNot(HaveOccurred()) dhcpConfig := fs.GetFileTestStat("/etc/dhcp/dhclient.conf") Expect(dhcpConfig).ToNot(BeNil()) Expect(dhcpConfig.StringContents()).To(Equal(expectedDhclientConfiguration)) dhcpConfigSymlink := fs.GetFileTestStat("/etc/dhcp/dhclient-ethdhcp.conf") Expect(dhcpConfigSymlink).ToNot(BeNil()) Expect(dhcpConfigSymlink.SymlinkTarget).To(Equal("/etc/dhcp/dhclient.conf")) }) It("writes a dhcp configuration without prepended dns servers if there are no dns servers specified", func() { dhcpNetworkWithoutDNS := boshsettings.Network{ Type: "dynamic", Mac: "fake-dhcp-mac-address", } stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, }) err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetworkWithoutDNS}, nil) Expect(err).ToNot(HaveOccurred()) dhcpConfig := fs.GetFileTestStat("/etc/dhcp/dhclient.conf") Expect(dhcpConfig).ToNot(BeNil()) Expect(dhcpConfig.StringContents()).To(Equal(`# Generated by bosh-agent option rfc3442-classless-static-routes code 121 = array of unsigned integer 8; send host-name "<hostname>"; request subnet-mask, broadcast-address, time-offset, routers, domain-name, domain-name-servers, domain-search, host-name, netbios-name-servers, netbios-scope, interface-mtu, rfc3442-classless-static-routes, ntp-servers; `)) dhcpConfigSymlink := fs.GetFileTestStat("/etc/dhcp/dhclient-ethdhcp.conf") Expect(dhcpConfigSymlink).ToNot(BeNil()) Expect(dhcpConfigSymlink.SymlinkTarget).To(Equal("/etc/dhcp/dhclient.conf")) }) It("returns an error if it can't write a dhcp configuration", func() { stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) fs.WriteFileErrors["/etc/dhcp/dhclient.conf"] = errors.New("dhclient.conf-write-error") err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("dhclient.conf-write-error")) }) It("returns an error if it can't symlink a dhcp configuration", func() { stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) fs.SymlinkError = errors.New("dhclient-ethdhcp.conf-symlink-error") err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("dhclient-ethdhcp.conf-symlink-error")) }) It("doesn't write a dhcp configuration if there are no dhcp networks", func() { stubInterfaces(map[string]boshsettings.Network{ "ethstatic": staticNetwork, }) err := netManager.SetupNetworking(boshsettings.Networks{"static-network": staticNetwork}, nil) Expect(err).ToNot(HaveOccurred()) dhcpConfig := fs.GetFileTestStat("/etc/dhcp/dhclient-ethdhcp.conf") Expect(dhcpConfig).To(BeNil()) }) It("restarts the networks if any ifconfig file changes", func() { changingStaticNetwork := boshsettings.Network{ Type: "manual", IP: "1.2.3.5", Netmask: "255.255.255.0", Gateway: "3.4.5.6", Mac: "ethstatict-that-changes", } stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic-that-changes": changingStaticNetwork, "ethstatic": staticNetwork, }) fs.WriteFileString("/etc/sysconfig/network-scripts/ifcfg-ethstatic", expectedNetworkConfigurationForStatic) fs.WriteFileString("/etc/dhcp/dhclient.conf", expectedDhclientConfiguration) err := netManager.SetupNetworking(boshsettings.Networks{ "dhcp-network": dhcpNetwork, "changing-static-network": changingStaticNetwork, "static-network": staticNetwork, }, nil) Expect(err).ToNot(HaveOccurred()) Expect(len(cmdRunner.RunCommands)).To(Equal(1)) Expect(cmdRunner.RunCommands[0]).To(Equal([]string{"service", "network", "restart"})) }) It("doesn't restart the networks if ifcfg and /etc/dhcp/dhclient.conf don't change", func() { stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) fs.WriteFileString("/etc/sysconfig/network-scripts/ifcfg-ethstatic", expectedNetworkConfigurationForStatic) fs.WriteFileString("/etc/sysconfig/network-scripts/ifcfg-ethdhcp", expectedNetworkConfigurationForDHCP) fs.WriteFileString("/etc/dhcp/dhclient.conf", expectedDhclientConfiguration) err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).ToNot(HaveOccurred()) networkConfig := fs.GetFileTestStat("/etc/sysconfig/network-scripts/ifcfg-ethstatic") Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(expectedNetworkConfigurationForStatic)) dhcpConfig := fs.GetFileTestStat("/etc/dhcp/dhclient.conf") Expect(dhcpConfig.StringContents()).To(Equal(expectedDhclientConfiguration)) Expect(len(cmdRunner.RunCommands)).To(Equal(0)) }) It("restarts the networks if /etc/dhcp/dhclient.conf changes", func() { stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) fs.WriteFileString("/etc/sysconfig/network-scripts/ifcfg-ethstatic", expectedNetworkConfigurationForStatic) err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).ToNot(HaveOccurred()) networkConfig := fs.GetFileTestStat("/etc/sysconfig/network-scripts/ifcfg-ethstatic") Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(expectedNetworkConfigurationForStatic)) Expect(len(cmdRunner.RunCommands)).To(Equal(1)) Expect(cmdRunner.RunCommands[0]).To(Equal([]string{"service", "network", "restart"})) }) It("broadcasts MAC addresses for all interfaces", func() { stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) errCh := make(chan error) err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, errCh) Expect(err).ToNot(HaveOccurred()) broadcastErr := <-errCh // wait for all arpings Expect(broadcastErr).ToNot(HaveOccurred()) Expect(addressBroadcaster.BroadcastMACAddressesAddresses).To(Equal([]boship.InterfaceAddress{ boship.NewSimpleInterfaceAddress("ethstatic", "1.2.3.4"), boship.NewResolvingInterfaceAddress("ethdhcp", ipResolver), })) }) It("skips vip networks", func() { stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) vipNetwork := boshsettings.Network{ Type: "vip", Default: []string{"dns"}, DNS: []string{"4.4.4.4", "5.5.5.5"}, Mac: "fake-vip-mac-address", IP: "9.8.7.6", } err := netManager.SetupNetworking(boshsettings.Networks{ "dhcp-network": dhcpNetwork, "static-network": staticNetwork, "vip-network": vipNetwork, }, nil) Expect(err).ToNot(HaveOccurred()) networkConfig := fs.GetFileTestStat("/etc/sysconfig/network-scripts/ifcfg-ethstatic") Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(expectedNetworkConfigurationForStatic)) }) It("doesn't use vip networks dns", func() { stubInterfaces(map[string]boshsettings.Network{ "ethstatic": staticNetwork, }) vipNetwork := boshsettings.Network{ Type: "vip", Default: []string{"dns"}, DNS: []string{"4.4.4.4", "5.5.5.5"}, Mac: "fake-vip-mac-address", IP: "9.8.7.6", } err := netManager.SetupNetworking(boshsettings.Networks{ "vip-network": vipNetwork, "static-network": staticNetwork, }, nil) Expect(err).ToNot(HaveOccurred()) networkConfig := fs.GetFileTestStat("/etc/sysconfig/network-scripts/ifcfg-ethstatic") Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).ToNot(ContainSubstring("4.4.4.4")) Expect(networkConfig.StringContents()).ToNot(ContainSubstring("5.5.5.5")) }) Context("when no MAC address is provided in the settings", func() { var staticNetworkWithoutMAC boshsettings.Network BeforeEach(func() { staticNetworkWithoutMAC = boshsettings.Network{ Type: "manual", IP: "1.2.3.4", Netmask: "255.255.255.0", Gateway: "3.4.5.6", DNS: []string{"8.8.8.8", "9.9.9.9"}, Default: []string{"dns"}, } }) It("configures network for single device", func() { stubInterfaces( map[string]boshsettings.Network{ "ethstatic": staticNetwork, }, ) err := netManager.SetupNetworking(boshsettings.Networks{ "static-network": staticNetworkWithoutMAC, }, nil) Expect(err).ToNot(HaveOccurred()) networkConfig := fs.GetFileTestStat("/etc/sysconfig/network-scripts/ifcfg-ethstatic") Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(expectedNetworkConfigurationForStatic)) }) It("configures network for single device, when a virtual device is also present", func() { stubInterfacesWithVirtual( map[string]boshsettings.Network{ "ethstatic": staticNetwork, }, []string{"virtual"}, ) err := netManager.SetupNetworking(boshsettings.Networks{ "static-network": staticNetworkWithoutMAC, }, nil) Expect(err).ToNot(HaveOccurred()) physicalNetworkConfig := fs.GetFileTestStat("/etc/sysconfig/network-scripts/ifcfg-ethstatic") Expect(physicalNetworkConfig).ToNot(BeNil()) Expect(physicalNetworkConfig.StringContents()).To(Equal(expectedNetworkConfigurationForStatic)) virtualNetworkConfig := fs.GetFileTestStat("/etc/sysconfig/network-scripts/ifcfg-virtual") Expect(virtualNetworkConfig).To(BeNil()) }) }) }) Describe("GetConfiguredNetworkInterfaces", func() { Context("when there are network devices", func() { BeforeEach(func() { interfacePaths := []string{} interfacePaths = append(interfacePaths, writeNetworkDevice("fake-eth0", "aa:bb", true)) interfacePaths = append(interfacePaths, writeNetworkDevice("fake-eth1", "cc:dd", true)) interfacePaths = append(interfacePaths, writeNetworkDevice("fake-eth2", "ee:ff", true)) fs.SetGlob("/sys/class/net/*", interfacePaths) }) writeIfcgfFile := func(iface string) { fs.WriteFileString(fmt.Sprintf("/etc/sysconfig/network-scripts/ifcfg-%s", iface), "fake-config") } It("returns networks that have ifcfg config present", func() { writeIfcgfFile("fake-eth0") writeIfcgfFile("fake-eth2") interfaces, err := netManager.GetConfiguredNetworkInterfaces() Expect(err).ToNot(HaveOccurred()) Expect(interfaces).To(ConsistOf("fake-eth0", "fake-eth2")) }) }) Context("when there are no network devices", func() { It("returns empty list", func() { interfaces, err := netManager.GetConfiguredNetworkInterfaces() Expect(err).ToNot(HaveOccurred()) Expect(interfaces).To(Equal([]string{})) }) }) }) }
func init() { Describe("renderedJobApplier", func() { var ( jobsBc *fakebc.FakeBundleCollection jobSupervisor *fakejobsuper.FakeJobSupervisor packageApplierProvider *fakepackages.FakeApplierProvider blobstore *fakeblob.FakeBlobstore compressor *fakecmd.FakeCompressor fs *fakesys.FakeFileSystem applier Applier ) BeforeEach(func() { jobsBc = fakebc.NewFakeBundleCollection() jobSupervisor = fakejobsuper.NewFakeJobSupervisor() packageApplierProvider = fakepackages.NewFakeApplierProvider() blobstore = fakeblob.NewFakeBlobstore() fs = fakesys.NewFakeFileSystem() compressor = fakecmd.NewFakeCompressor() logger := boshlog.NewLogger(boshlog.LevelNone) applier = NewRenderedJobApplier( jobsBc, jobSupervisor, packageApplierProvider, blobstore, compressor, fs, logger, ) }) Describe("Prepare & Apply", func() { var ( job models.Job bundle *fakebc.FakeBundle ) BeforeEach(func() { job, bundle = buildJob(jobsBc) }) ItInstallsJob := func(act func() error) { BeforeEach(func() { fs.TempDirDir = "/fake-tmp-dir" }) It("returns error when installing job fails", func() { bundle.InstallError = errors.New("fake-install-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-install-error")) }) It("downloads and later cleans up downloaded job template blob", func() { blobstore.GetFileName = "/fake-blobstore-file-name" err := act() Expect(err).ToNot(HaveOccurred()) Expect(blobstore.GetBlobIDs[0]).To(Equal("fake-blobstore-id")) Expect(blobstore.GetFingerprints[0]).To(Equal("fake-blob-sha1")) // downloaded file is cleaned up Expect(blobstore.CleanUpFileName).To(Equal("/fake-blobstore-file-name")) }) It("returns error when downloading job template blob fails", func() { blobstore.GetError = errors.New("fake-get-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-get-error")) }) It("decompresses job template blob to tmp path and later cleans it up", func() { blobstore.GetFileName = "/fake-blobstore-file-name" var tmpDirExistsBeforeInstall bool bundle.InstallCallBack = func() { tmpDirExistsBeforeInstall = true } err := act() Expect(err).ToNot(HaveOccurred()) Expect(compressor.DecompressFileToDirTarballPaths[0]).To(Equal("/fake-blobstore-file-name")) Expect(compressor.DecompressFileToDirDirs[0]).To(Equal("/fake-tmp-dir")) // tmp dir exists before bundle install Expect(tmpDirExistsBeforeInstall).To(BeTrue()) // tmp dir is cleaned up after install Expect(fs.FileExists(fs.TempDirDir)).To(BeFalse()) }) It("returns error when temporary directory creation fails", func() { fs.TempDirError = errors.New("fake-filesystem-tempdir-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-filesystem-tempdir-error")) }) It("returns error when decompressing job template fails", func() { compressor.DecompressFileToDirErr = errors.New("fake-decompress-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-decompress-error")) }) It("returns error when walking the tree of files fails", func() { fs.WalkErr = errors.New("fake-walk-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-walk-error")) }) It("installs bundle from decompressed tmp path of a job template", func() { var installedBeforeDecompression bool compressor.DecompressFileToDirCallBack = func() { installedBeforeDecompression = bundle.Installed } err := act() Expect(err).ToNot(HaveOccurred()) // bundle installation did not happen before decompression Expect(installedBeforeDecompression).To(BeFalse()) // make sure that bundle install happened after decompression Expect(bundle.InstallSourcePath).To(Equal("/fake-tmp-dir/fake-path-in-archive")) }) It("sets executable bit for the bin and config directories", func() { var binDirStats, configDirStats *fakesys.FakeFileStats compressor.DecompressFileToDirCallBack = func() { fs.WriteFile("/fake-tmp-dir/fake-path-in-archive/bin/blarg", []byte{}) fs.WriteFile("/fake-tmp-dir/fake-path-in-archive/config/blarg.yml", []byte{}) } bundle.InstallCallBack = func() { binDirStats = fs.GetFileTestStat("/fake-tmp-dir/fake-path-in-archive/bin") configDirStats = fs.GetFileTestStat("/fake-tmp-dir/fake-path-in-archive/config") } err := act() Expect(err).ToNot(HaveOccurred()) Expect(int(binDirStats.FileMode)).To(Equal(0755)) Expect(int(configDirStats.FileMode)).To(Equal(0755)) }) It("sets executable bit for files in bin", func() { compressor.DecompressFileToDirCallBack = func() { fs.WriteFile("/fake-tmp-dir/fake-path-in-archive/bin/test1", []byte{}) fs.WriteFile("/fake-tmp-dir/fake-path-in-archive/bin/test2", []byte{}) fs.WriteFile("/fake-tmp-dir/fake-path-in-archive/config/test", []byte{}) } fs.SetGlob("/fake-tmp-dir/fake-path-in-archive/bin/*", []string{ "/fake-tmp-dir/fake-path-in-archive/bin/test1", "/fake-tmp-dir/fake-path-in-archive/bin/test2", }) var binTest1Stats, binTest2Stats, configTestStats *fakesys.FakeFileStats bundle.InstallCallBack = func() { binTest1Stats = fs.GetFileTestStat("/fake-tmp-dir/fake-path-in-archive/bin/test1") binTest2Stats = fs.GetFileTestStat("/fake-tmp-dir/fake-path-in-archive/bin/test2") configTestStats = fs.GetFileTestStat("/fake-tmp-dir/fake-path-in-archive/config/test") } err := act() Expect(err).ToNot(HaveOccurred()) // bin files are executable Expect(int(binTest1Stats.FileMode)).To(Equal(0755)) Expect(int(binTest2Stats.FileMode)).To(Equal(0755)) // non-bin files are not made executable Expect(int(configTestStats.FileMode)).ToNot(Equal(0755)) }) It("sets 644 permissions for files in config", func() { compressor.DecompressFileToDirCallBack = func() { fs.WriteFile("/fake-tmp-dir/fake-path-in-archive/config/config1", []byte{}) fs.WriteFile("/fake-tmp-dir/fake-path-in-archive/config/config2", []byte{}) } fs.SetGlob("/fake-tmp-dir/fake-path-in-archive/config/*", []string{ "/fake-tmp-dir/fake-path-in-archive/config/config1", "/fake-tmp-dir/fake-path-in-archive/config/config2", }) var config1Stats, config2Stats *fakesys.FakeFileStats bundle.InstallCallBack = func() { config1Stats = fs.GetFileTestStat("/fake-tmp-dir/fake-path-in-archive/config/config1") config2Stats = fs.GetFileTestStat("/fake-tmp-dir/fake-path-in-archive/config/config2") } err := act() Expect(err).ToNot(HaveOccurred()) // permission for config files should be readable by all Expect(int(config1Stats.FileMode)).To(Equal(0644)) Expect(int(config2Stats.FileMode)).To(Equal(0644)) }) } ItUpdatesPackages := func(act func() error) { var packageApplier *fakepackages.FakeApplier BeforeEach(func() { packageApplier = fakepackages.NewFakeApplier() packageApplierProvider.JobSpecificAppliers[job.Name] = packageApplier }) It("applies each package that job depends on and then cleans up packages", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(packageApplier.ActionsCalled).To(Equal([]string{"Apply", "Apply", "KeepOnly"})) Expect(len(packageApplier.AppliedPackages)).To(Equal(2)) // present Expect(packageApplier.AppliedPackages).To(Equal(job.Packages)) }) It("returns error when applying package that job depends on fails", func() { packageApplier.ApplyError = errors.New("fake-apply-err") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-apply-err")) }) It("keeps only currently required packages but does not completely uninstall them", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(len(packageApplier.KeptOnlyPackages)).To(Equal(2)) // present Expect(packageApplier.KeptOnlyPackages).To(Equal(job.Packages)) }) It("returns error when keeping only currently required packages fails", func() { packageApplier.KeepOnlyErr = errors.New("fake-keep-only-err") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-keep-only-err")) }) } Describe("Prepare", func() { act := func() error { return applier.Prepare(job) } It("return an error if getting file bundle fails", func() { jobsBc.GetErr = errors.New("fake-get-bundle-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-get-bundle-error")) }) It("returns an error if checking for installed path fails", func() { bundle.IsInstalledErr = errors.New("fake-is-installed-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-is-installed-error")) }) Context("when job is already installed", func() { BeforeEach(func() { bundle.Installed = true }) It("does not install", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(bundle.ActionsCalled).To(Equal([]string{})) // no Install }) It("does not download the job template", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(blobstore.GetBlobIDs).To(BeNil()) }) }) Context("when job is not installed", func() { BeforeEach(func() { bundle.Installed = false }) It("installs job (but does not enable)", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(bundle.ActionsCalled).To(Equal([]string{"Install"})) }) ItInstallsJob(act) }) }) Describe("Apply", func() { act := func() error { return applier.Apply(job) } It("return an error if getting file bundle fails", func() { jobsBc.GetErr = errors.New("fake-get-bundle-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-get-bundle-error")) }) It("returns an error if checking for installed path fails", func() { bundle.IsInstalledErr = errors.New("fake-is-installed-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-is-installed-error")) }) Context("when job is already installed", func() { BeforeEach(func() { bundle.Installed = true }) It("does not install but only enables job", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(bundle.ActionsCalled).To(Equal([]string{"Enable"})) // no Install }) It("returns error when job enable fails", func() { bundle.EnableError = errors.New("fake-enable-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-enable-error")) }) It("does not download the job template", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(blobstore.GetBlobIDs).To(BeNil()) }) ItUpdatesPackages(act) }) Context("when job is not installed", func() { BeforeEach(func() { bundle.Installed = false }) It("installs and enables job", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(bundle.ActionsCalled).To(Equal([]string{"Install", "Enable"})) }) It("returns error when job enable fails", func() { bundle.EnableError = errors.New("fake-enable-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-enable-error")) }) ItInstallsJob(act) ItUpdatesPackages(act) }) }) }) Describe("Configure", func() { It("adds job to the job supervisor", func() { job, bundle := buildJob(jobsBc) fs := fakesys.NewFakeFileSystem() fs.WriteFileString("/path/to/job/monit", "some conf") fs.SetGlob("/path/to/job/*.monit", []string{"/path/to/job/subjob.monit"}) bundle.GetDirPath = "/path/to/job" bundle.GetDirFs = fs err := applier.Configure(job, 0) Expect(err).ToNot(HaveOccurred()) Expect(len(jobSupervisor.AddJobArgs)).To(Equal(2)) Expect(jobSupervisor.AddJobArgs[0]).To(Equal(fakejobsuper.AddJobArgs{ Name: job.Name, Index: 0, ConfigPath: "/path/to/job/monit", })) Expect(jobSupervisor.AddJobArgs[1]).To(Equal(fakejobsuper.AddJobArgs{ Name: job.Name + "_subjob", Index: 0, ConfigPath: "/path/to/job/subjob.monit", })) }) It("does not require monit script", func() { job, bundle := buildJob(jobsBc) fs := fakesys.NewFakeFileSystem() bundle.GetDirFs = fs err := applier.Configure(job, 0) Expect(err).ToNot(HaveOccurred()) Expect(len(jobSupervisor.AddJobArgs)).To(Equal(0)) }) }) Describe("KeepOnly", func() { It("first disables and then uninstalls jobs that are not in keeponly list", func() { _, bundle1 := buildJob(jobsBc) job2, bundle2 := buildJob(jobsBc) _, bundle3 := buildJob(jobsBc) job4, bundle4 := buildJob(jobsBc) jobsBc.ListBundles = []boshbc.Bundle{bundle1, bundle2, bundle3, bundle4} err := applier.KeepOnly([]models.Job{job4, job2}) Expect(err).ToNot(HaveOccurred()) Expect(bundle1.ActionsCalled).To(Equal([]string{"Disable", "Uninstall"})) Expect(bundle2.ActionsCalled).To(Equal([]string{})) Expect(bundle3.ActionsCalled).To(Equal([]string{"Disable", "Uninstall"})) Expect(bundle4.ActionsCalled).To(Equal([]string{})) }) It("returns error when bundle collection fails to return list of installed bundles", func() { jobsBc.ListErr = errors.New("fake-bc-list-error") err := applier.KeepOnly([]models.Job{}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-bc-list-error")) }) It("returns error when bundle collection cannot retrieve bundle for keep-only job", func() { job1, bundle1 := buildJob(jobsBc) jobsBc.ListBundles = []boshbc.Bundle{bundle1} jobsBc.GetErr = errors.New("fake-bc-get-error") err := applier.KeepOnly([]models.Job{job1}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-bc-get-error")) }) It("returns error when at least one bundle cannot be disabled", func() { _, bundle1 := buildJob(jobsBc) jobsBc.ListBundles = []boshbc.Bundle{bundle1} bundle1.DisableErr = errors.New("fake-bc-disable-error") err := applier.KeepOnly([]models.Job{}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-bc-disable-error")) }) It("returns error when at least one bundle cannot be uninstalled", func() { _, bundle1 := buildJob(jobsBc) jobsBc.ListBundles = []boshbc.Bundle{bundle1} bundle1.UninstallErr = errors.New("fake-bc-uninstall-error") err := applier.KeepOnly([]models.Job{}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-bc-uninstall-error")) }) }) }) }
result := cert.SplitCerts("abcdefghij") Expect(len(result)).To(Equal(0)) }) }) Describe("DeleteFile()", func() { var ( fakeFs *fakesys.FakeFileSystem ) BeforeEach(func() { fakeFs = fakesys.NewFakeFileSystem() }) It("only deletes the files with the given prefix", func() { fakeFs.WriteFileString("/path/to/delete/stuff/in/delete_me_1.foo", "goodbye") fakeFs.WriteFileString("/path/to/delete/stuff/in/delete_me_2.foo", "goodbye") fakeFs.WriteFileString("/path/to/delete/stuff/in/different_file_1.bar", "goodbye") fakeFs.SetGlob("/path/to/delete/stuff/in/delete_me_*", []string{ "/path/to/delete/stuff/in/delete_me_1.foo", "/path/to/delete/stuff/in/delete_me_2.foo", }) count, err := cert.DeleteFiles(fakeFs, "/path/to/delete/stuff/in/", "delete_me_") Expect(err).ToNot(HaveOccurred()) Expect(count).To(Equal(2)) Expect(countFiles(fakeFs, "/path/to/delete/stuff/in/")).To(Equal(1)) }) It("only deletes the files in the given path", func() { fakeFs.WriteFileString("/path/to/delete/stuff/in/delete_me_1.foo", "goodbye") fakeFs.WriteFileString("/path/to/delete/stuff/in/delete_me_2.foo", "goodbye")
fs *fakesys.FakeFileSystem searcher MountsSearcher ) BeforeEach(func() { fs = fakesys.NewFakeFileSystem() searcher = NewProcMountsSearcher(fs) }) Describe("SearchMounts", func() { Context("when reading /proc/mounts succeeds", func() { It("returns parsed mount information", func() { fs.WriteFileString( "/proc/mounts", `none /run/lock tmpfs rw,nosuid,nodev,noexec,relatime,size=5120k 0 0 none /run/shm tmpfs rw,nosuid,nodev,relatime 0 0 /dev/sda1 /boot ext2 rw,relatime,errors=continue 0 0 none /tmp/warden/cgroup tmpfs rw,relatime 0 0`, ) mounts, err := searcher.SearchMounts() Expect(err).ToNot(HaveOccurred()) Expect(mounts).To(Equal([]Mount{ Mount{PartitionPath: "none", MountPoint: "/run/lock"}, Mount{PartitionPath: "none", MountPoint: "/run/shm"}, Mount{PartitionPath: "/dev/sda1", MountPoint: "/boot"}, Mount{PartitionPath: "none", MountPoint: "/tmp/warden/cgroup"}, })) }) It("ignores empty lines", func() {
fs.WriteFileString("/fake-config.conf", `{ "Platform": { "Linux": { "UseDefaultTmpDir": true, "UsePreformattedPersistentDisk": true, "BindMountPersistentDisk": true, "DevicePathResolutionType": "virtio" } }, "Infrastructure": { "Settings": { "Sources": [ { "Type": "HTTP", "URI": "http://fake-uri" }, { "Type": "ConfigDrive", "DiskPaths": ["/fake-disk-path1", "/fake-disk-path2"], "MetaDataPath": "/fake-metadata-path", "UserDataPath": "/fake-userdata-path", "SettingsPath": "/fake-settings-path" }, { "Type": "File", "MetaDataPath": "/fake-metadata-path", "UserDataPath": "/fake-userdata-path", "SettingsPath": "/fake-settings-path" }, { "Type": "CDROM", "FileName": "/fake-file-name" } ], "UseServerName": true, "UseRegistry": true } } }`)