func NewFakePlatform() (platform *FakePlatform) { platform = new(FakePlatform) platform.Fs = fakesys.NewFakeFileSystem() platform.Runner = fakesys.NewFakeCmdRunner() platform.FakeCompressor = fakecmd.NewFakeCompressor() platform.FakeCopier = fakecmd.NewFakeCopier() platform.FakeVitalsService = fakevitals.NewFakeService() platform.DevicePathResolver = fakedpresolv.NewFakeDevicePathResolver() platform.AddUserToGroupsGroups = make(map[string][]string) platform.SetupSSHPublicKeys = make(map[string][]string) platform.UserPasswords = make(map[string]string) platform.ScsiDiskMap = make(map[string]string) platform.GetFileContentsFromDiskDiskPaths = []string{} platform.GetFileContentsFromDiskFileNames = [][]string{} platform.GetFileContentsFromDiskContents = map[string][]byte{} platform.GetFileContentsFromDiskErrs = map[string]error{} platform.certManager = new(fakecert.FakeManager) platform.SetupRawEphemeralDisksCallCount = 0 platform.SetupRawEphemeralDisksDevices = nil platform.SetupRawEphemeralDisksErr = nil platform.GetHostPublicKeyError = nil platform.SetupRootDiskCalledTimes = 0 platform.SetupRootDiskError = nil platform.IsPersistentDiskMountableErr = nil return }
func describeDummyPlatform() { var ( platform Platform collector boshstats.Collector fs boshsys.FileSystem cmdRunner boshsys.CmdRunner dirProvider boshdirs.Provider devicePathResolver boshdpresolv.DevicePathResolver logger boshlog.Logger ) BeforeEach(func() { collector = &fakestats.FakeCollector{} fs = fakesys.NewFakeFileSystem() cmdRunner = fakesys.NewFakeCmdRunner() dirProvider = boshdirs.NewProvider("/fake-dir") devicePathResolver = fakedpresolv.NewFakeDevicePathResolver() logger = boshlog.NewLogger(boshlog.LevelNone) }) JustBeforeEach(func() { platform = NewDummyPlatform( collector, fs, cmdRunner, dirProvider, devicePathResolver, logger, ) }) Describe("GetDefaultNetwork", func() { It("returns the contents of dummy-defaults-network-settings.json since that's what the dummy cpi writes", func() { settingsFilePath := "/fake-dir/bosh/dummy-default-network-settings.json" fs.WriteFileString(settingsFilePath, `{"IP": "1.2.3.4"}`) network, err := platform.GetDefaultNetwork() Expect(err).NotTo(HaveOccurred()) Expect(network.IP).To(Equal("1.2.3.4")) }) }) Describe("GetCertManager", func() { It("returs a dummy cert manager", func() { certManager := platform.GetCertManager() Expect(certManager.UpdateCertificates("")).Should(BeNil()) }) }) }
func init() { Describe("concreteManagerProvider", func() { Describe("NewManager", func() { It("returns manager with tasks.json as its tasks path", func() { logger := boshlog.NewLogger(boshlog.LevelNone) fs := fakesys.NewFakeFileSystem() taskInfo := boshtask.Info{ TaskID: "fake-task-id", Method: "fake-method", Payload: []byte("fake-payload"), } manager := boshtask.NewManagerProvider().NewManager(logger, fs, "/dir/path") err := manager.AddInfo(taskInfo) Expect(err).ToNot(HaveOccurred()) // Check expected file location with another manager otherManager := boshtask.NewManager(logger, fs, "/dir/path/tasks.json") taskInfos, err := otherManager.GetInfos() Expect(err).ToNot(HaveOccurred()) Expect(taskInfos).To(Equal([]boshtask.Info{taskInfo})) }) }) }) Describe("concreteManager", func() { var ( logger boshlog.Logger fs *fakesys.FakeFileSystem manager boshtask.Manager ) BeforeEach(func() { logger = boshlog.NewLogger(boshlog.LevelNone) fs = fakesys.NewFakeFileSystem() manager = boshtask.NewManager(logger, fs, "/dir/path") }) Describe("GetInfos", func() { It("can load multiple tasks", func() { err := manager.AddInfo(boshtask.Info{ TaskID: "fake-task-id-1", Method: "fake-method-1", Payload: []byte("fake-payload-1"), }) Expect(err).ToNot(HaveOccurred()) err = manager.AddInfo(boshtask.Info{ TaskID: "fake-task-id-2", Method: "fake-method-2", Payload: []byte("fake-payload-2"), }) Expect(err).ToNot(HaveOccurred()) // Make sure we are not getting cached copy of taskInfos reloadedManager := boshtask.NewManager(logger, fs, "/dir/path") taskInfos, err := reloadedManager.GetInfos() Expect(err).ToNot(HaveOccurred()) Expect(taskInfos).To(ConsistOf([]boshtask.Info{ boshtask.Info{ TaskID: "fake-task-id-1", Method: "fake-method-1", Payload: []byte("fake-payload-1"), }, boshtask.Info{ TaskID: "fake-task-id-2", Method: "fake-method-2", Payload: []byte("fake-payload-2"), }, })) }) It("succeeds when there is no tasks (file is not present)", func() { taskInfos, err := manager.GetInfos() Expect(err).ToNot(HaveOccurred()) Expect(len(taskInfos)).To(Equal(0)) }) It("returns an error when failing to load tasks from the file that exists", func() { err := manager.AddInfo(boshtask.Info{ TaskID: "fake-task-id-2", Method: "fake-method-2", Payload: []byte("fake-payload-2"), }) Expect(err).ToNot(HaveOccurred()) fs.ReadFileError = errors.New("fake-read-error") _, err = manager.GetInfos() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-read-error")) }) }) Describe("AddInfo", func() { It("can add multiple tasks", func() { err := manager.AddInfo(boshtask.Info{ TaskID: "fake-task-id-1", Method: "fake-method-1", Payload: []byte("fake-payload-1"), }) Expect(err).ToNot(HaveOccurred()) err = manager.AddInfo(boshtask.Info{ TaskID: "fake-task-id-2", Method: "fake-method-2", Payload: []byte("fake-payload-2"), }) Expect(err).ToNot(HaveOccurred()) content, err := fs.ReadFile("/dir/path") Expect(err).ToNot(HaveOccurred()) var decodedMap map[string]boshtask.Info err = json.Unmarshal(content, &decodedMap) Expect(err).ToNot(HaveOccurred()) Expect(decodedMap).To(Equal(map[string]boshtask.Info{ "fake-task-id-1": boshtask.Info{ TaskID: "fake-task-id-1", Method: "fake-method-1", Payload: []byte("fake-payload-1"), }, "fake-task-id-2": boshtask.Info{ TaskID: "fake-task-id-2", Method: "fake-method-2", Payload: []byte("fake-payload-2"), }, })) }) It("returns an error when failing to save task", func() { fs.WriteFileError = errors.New("fake-write-error") err := manager.AddInfo(boshtask.Info{ TaskID: "fake-task-id", Method: "fake-method", Payload: []byte("fake-payload"), }) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-write-error")) }) }) Describe("RemoveInfo", func() { BeforeEach(func() { err := manager.AddInfo(boshtask.Info{ TaskID: "fake-task-id-1", Method: "fake-method-1", Payload: []byte("fake-payload-1"), }) Expect(err).ToNot(HaveOccurred()) err = manager.AddInfo(boshtask.Info{ TaskID: "fake-task-id-2", Method: "fake-method-2", Payload: []byte("fake-payload-2"), }) Expect(err).ToNot(HaveOccurred()) }) It("removes the task", func() { err := manager.RemoveInfo("fake-task-id-1") Expect(err).ToNot(HaveOccurred()) content, err := fs.ReadFile("/dir/path") Expect(err).ToNot(HaveOccurred()) var decodedMap map[string]boshtask.Info err = json.Unmarshal(content, &decodedMap) Expect(err).ToNot(HaveOccurred()) Expect(decodedMap).To(Equal(map[string]boshtask.Info{ "fake-task-id-2": boshtask.Info{ TaskID: "fake-task-id-2", Method: "fake-method-2", Payload: []byte("fake-payload-2"), }, })) }) It("does not return error when removing task that does not exist", func() { err := manager.RemoveInfo("fake-unknown-task-id") Expect(err).ToNot(HaveOccurred()) }) It("returns an error when failing to remove task", func() { fs.WriteFileError = errors.New("fake-write-error") err := manager.RemoveInfo("fake-task-id") Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-write-error")) }) }) }) }
. "github.com/onsi/gomega" "github.com/cloudfoundry/bosh-utils/system/fakes" ) var _ = Describe("SyncDNSState", func() { var ( localDNSState LocalDNSState syncDNSState SyncDNSState fakeFileSystem *fakes.FakeFileSystem path string err error ) BeforeEach(func() { fakeFileSystem = fakes.NewFakeFileSystem() path = "/blobstore-dns-records.json" syncDNSState = NewSyncDNSState(fakeFileSystem, path) err = nil localDNSState = LocalDNSState{} }) Describe("#LoadState", func() { Context("when there is some failure loading", func() { Context("when SyncDNSState file cannot be read", func() { It("should fail loading DNS state", func() { _, err = syncDNSState.LoadState() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("reading state file")) }) })
func init() { Describe("bootstrap", func() { Describe("Run", func() { var ( platform *fakeplatform.FakePlatform dirProvider boshdir.Provider settingsSource *fakeinf.FakeSettingsSource settingsService *fakesettings.FakeSettingsService ) BeforeEach(func() { platform = fakeplatform.NewFakePlatform() dirProvider = boshdir.NewProvider("/var/vcap") settingsSource = &fakeinf.FakeSettingsSource{} settingsService = &fakesettings.FakeSettingsService{} }) bootstrap := func() error { logger := boshlog.NewLogger(boshlog.LevelNone) return NewBootstrap(platform, dirProvider, settingsService, logger).Run() } It("sets up runtime configuration", func() { err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(platform.SetupRuntimeConfigurationWasInvoked).To(BeTrue()) }) Describe("SSH tunnel setup for registry", func() { It("returns error without configuring ssh on the platform if getting public key fails", func() { settingsService.PublicKeyErr = errors.New("fake-get-public-key-err") err := bootstrap() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-get-public-key-err")) Expect(platform.SetupSSHCalled).To(BeFalse()) }) Context("when public key is not empty", func() { BeforeEach(func() { settingsService.PublicKey = "fake-public-key" }) It("gets the public key and sets up ssh via the platform", func() { err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(platform.SetupSSHPublicKey).To(Equal("fake-public-key")) Expect(platform.SetupSSHUsername).To(Equal("vcap")) }) It("returns error if configuring ssh on the platform fails", func() { platform.SetupSSHErr = errors.New("fake-setup-ssh-err") err := bootstrap() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-setup-ssh-err")) }) }) Context("when public key key is empty", func() { BeforeEach(func() { settingsSource.PublicKey = "" }) It("gets the public key and does not setup SSH", func() { err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(platform.SetupSSHCalled).To(BeFalse()) }) }) }) It("sets up hostname", func() { settingsService.Settings.AgentID = "foo-bar-baz-123" err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(platform.SetupHostnameHostname).To(Equal("foo-bar-baz-123")) }) It("fetches initial settings", func() { err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(settingsService.SettingsWereLoaded).To(BeTrue()) }) It("returns error from loading initial settings", func() { settingsService.LoadSettingsError = errors.New("fake-load-error") err := bootstrap() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-load-error")) }) It("sets up networking", func() { networks := boshsettings.Networks{ "bosh": boshsettings.Network{}, } settingsService.Settings.Networks = networks err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(platform.SetupNetworkingNetworks).To(Equal(networks)) }) It("sets up ephemeral disk", func() { settingsService.Settings.Disks = boshsettings.Disks{ Ephemeral: "fake-ephemeral-disk-setting", } platform.GetEphemeralDiskPathRealPath = "/dev/sda" err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(platform.SetupEphemeralDiskWithPathDevicePath).To(Equal("/dev/sda")) Expect(platform.GetEphemeralDiskPathSettings).To(Equal(boshsettings.DiskSettings{ VolumeID: "fake-ephemeral-disk-setting", Path: "fake-ephemeral-disk-setting", })) }) It("returns error if setting ephemeral disk fails", func() { platform.SetupEphemeralDiskWithPathErr = errors.New("fake-setup-ephemeral-disk-err") err := bootstrap() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-setup-ephemeral-disk-err")) }) It("sets up raw ephemeral disks if paths exist", func() { settingsService.Settings.Disks = boshsettings.Disks{ RawEphemeral: []boshsettings.DiskSettings{{Path: "/dev/xvdb"}, {Path: "/dev/xvdc"}}, } err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(platform.SetupRawEphemeralDisksCallCount).To(Equal(1)) Expect(len(platform.SetupRawEphemeralDisksDevices)).To(Equal(2)) Expect(platform.SetupRawEphemeralDisksDevices[0].Path).To(Equal("/dev/xvdb")) Expect(platform.SetupRawEphemeralDisksDevices[1].Path).To(Equal("/dev/xvdc")) }) It("returns error if setting raw ephemeral disks fails", func() { platform.SetupRawEphemeralDisksErr = errors.New("fake-setup-raw-ephemeral-disks-err") err := bootstrap() Expect(platform.SetupRawEphemeralDisksCallCount).To(Equal(1)) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-setup-raw-ephemeral-disks-err")) }) It("sets up data dir", func() { err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(platform.SetupDataDirCalled).To(BeTrue()) }) It("returns error if set up of data dir fails", func() { platform.SetupDataDirErr = errors.New("fake-setup-data-dir-err") err := bootstrap() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-setup-data-dir-err")) }) It("sets up tmp dir", func() { err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(platform.SetupTmpDirCalled).To(BeTrue()) }) It("returns error if set up of tmp dir fails", func() { platform.SetupTmpDirErr = errors.New("fake-setup-tmp-dir-err") err := bootstrap() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-setup-tmp-dir-err")) }) It("mounts persistent disk", func() { settingsService.Settings.Disks = boshsettings.Disks{ Persistent: map[string]interface{}{ "vol-123": map[string]interface{}{ "volume_id": "2", "path": "/dev/sdb", }, }, } err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(platform.MountPersistentDiskSettings).To(Equal(boshsettings.DiskSettings{ ID: "vol-123", VolumeID: "2", Path: "/dev/sdb", })) Expect(platform.MountPersistentDiskMountPoint).To(Equal(dirProvider.StoreDir())) }) It("errors if there is more than one persistent disk", func() { settingsService.Settings.Disks = boshsettings.Disks{ Persistent: map[string]interface{}{ "vol-123": "/dev/sdb", "vol-456": "/dev/sdc", }, } err := bootstrap() Expect(err).To(HaveOccurred()) }) It("does not try to mount when no persistent disk", func() { settingsService.Settings.Disks = boshsettings.Disks{ Persistent: map[string]interface{}{}, } err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(platform.MountPersistentDiskSettings).To(Equal(boshsettings.DiskSettings{})) Expect(platform.MountPersistentDiskMountPoint).To(Equal("")) }) It("grows the root filesystem", func() { err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(platform.SetupRootDiskCalledTimes).To(Equal(1)) }) It("returns an error if growing the root filesystem fails", func() { platform.SetupRootDiskError = errors.New("growfs failed") err := bootstrap() Expect(err).To(HaveOccurred()) Expect(platform.SetupRootDiskCalledTimes).To(Equal(1)) Expect(err.Error()).To(ContainSubstring("growfs failed")) }) It("sets root and vcap passwords", func() { settingsService.Settings.Env.Bosh.Password = "******" settingsService.Settings.Env.Bosh.KeepRootPassword = false err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(2).To(Equal(len(platform.UserPasswords))) Expect("some-encrypted-password").To(Equal(platform.UserPasswords["root"])) Expect("some-encrypted-password").To(Equal(platform.UserPasswords["vcap"])) }) It("does not change root password if keep_root_password is set to true", func() { settingsService.Settings.Env.Bosh.Password = "******" settingsService.Settings.Env.Bosh.KeepRootPassword = true err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(1).To(Equal(len(platform.UserPasswords))) Expect("some-encrypted-password").ToNot(Equal(platform.UserPasswords["root"])) Expect("some-encrypted-password").To(Equal(platform.UserPasswords["vcap"])) }) It("does not set password if not provided", func() { settingsService.Settings.Env.Bosh.KeepRootPassword = false err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(0).To(Equal(len(platform.UserPasswords))) }) It("sets ntp", func() { settingsService.Settings.Ntp = []string{ "0.north-america.pool.ntp.org", "1.north-america.pool.ntp.org", } err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(2).To(Equal(len(platform.SetTimeWithNtpServersServers))) Expect("0.north-america.pool.ntp.org").To(Equal(platform.SetTimeWithNtpServersServers[0])) Expect("1.north-america.pool.ntp.org").To(Equal(platform.SetTimeWithNtpServersServers[1])) }) It("setups up monit user", func() { err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(platform.SetupMonitUserSetup).To(BeTrue()) }) It("starts monit", func() { err := bootstrap() Expect(err).NotTo(HaveOccurred()) Expect(platform.StartMonitStarted).To(BeTrue()) }) }) Describe("Network setup exercised by Run", func() { var ( settingsJSON string fs *fakesys.FakeFileSystem platform boshplatform.Platform boot Bootstrap defaultNetworkResolver boshsettings.DefaultNetworkResolver logger boshlog.Logger dirProvider boshdirs.Provider interfaceAddrsProvider *fakeip.FakeInterfaceAddressesProvider ) writeNetworkDevice := func(iface string, macAddress string, isPhysical bool) string { interfacePath := fmt.Sprintf("/sys/class/net/%s", iface) fs.WriteFile(interfacePath, []byte{}) if isPhysical { fs.WriteFile(fmt.Sprintf("/sys/class/net/%s/device", iface), []byte{}) } fs.WriteFileString(fmt.Sprintf("/sys/class/net/%s/address", iface), fmt.Sprintf("%s\n", macAddress)) return interfacePath } stubInterfaces := func(interfaces [][]string) { var interfacePaths []string for _, iface := range interfaces { interfaceName := iface[0] interfaceMAC := iface[1] interfaceType := iface[2] isPhysical := interfaceType == "physical" interfacePaths = append(interfacePaths, writeNetworkDevice(interfaceName, interfaceMAC, isPhysical)) } fs.SetGlob("/sys/class/net/*", interfacePaths) } BeforeEach(func() { fs = fakesys.NewFakeFileSystem() runner := fakesys.NewFakeCmdRunner() dirProvider = boshdirs.NewProvider("/var/vcap/bosh") linuxOptions := boshplatform.LinuxOptions{ CreatePartitionIfNoEphemeralDisk: true, } logger = boshlog.NewLogger(boshlog.LevelNone) diskManager := fakedisk.NewFakeDiskManager() diskManager.FakeMountsSearcher.SearchMountsMounts = []boshdisk.Mount{ {MountPoint: "/", PartitionPath: "rootfs"}, {MountPoint: "/", PartitionPath: "/dev/vda1"}, } // for the GrowRootFS call to findRootDevicePath runner.AddCmdResult( "readlink -f /dev/vda1", fakesys.FakeCmdResult{Stdout: "/dev/vda1"}, ) // for the createEphemeralPartitionsOnRootDevice call to findRootDevicePath runner.AddCmdResult( "readlink -f /dev/vda1", fakesys.FakeCmdResult{Stdout: "/dev/vda1"}, ) diskManager.FakeRootDevicePartitioner.GetDeviceSizeInBytesSizes["/dev/vda"] = 1024 * 1024 * 1024 udev := boshudev.NewConcreteUdevDevice(runner, logger) linuxCdrom := boshcdrom.NewLinuxCdrom("/dev/sr0", udev, runner) linuxCdutil := boshcdrom.NewCdUtil(dirProvider.SettingsDir(), fs, linuxCdrom, logger) compressor := boshcmd.NewTarballCompressor(runner, fs) copier := boshcmd.NewCpCopier(runner, fs, logger) sigarCollector := boshsigar.NewSigarStatsCollector(&sigar.ConcreteSigar{}) vitalsService := boshvitals.NewService(sigarCollector, dirProvider) ipResolver := boship.NewResolver(boship.NetworkInterfaceToAddrsFunc) arping := bosharp.NewArping(runner, fs, logger, boshplatform.ArpIterations, boshplatform.ArpIterationDelay, boshplatform.ArpInterfaceCheckDelay) interfaceConfigurationCreator := boshnet.NewInterfaceConfigurationCreator(logger) interfaceAddrsProvider = &fakeip.FakeInterfaceAddressesProvider{} interfaceAddressesValidator := boship.NewInterfaceAddressesValidator(interfaceAddrsProvider) dnsValidator := boshnet.NewDNSValidator(fs) fs.WriteFileString("/etc/resolv.conf", "8.8.8.8 4.4.4.4") ubuntuNetManager := boshnet.NewUbuntuNetManager(fs, runner, ipResolver, interfaceConfigurationCreator, interfaceAddressesValidator, dnsValidator, arping, logger) ubuntuCertManager := boshcert.NewUbuntuCertManager(fs, runner, 1, logger) monitRetryable := boshplatform.NewMonitRetryable(runner) monitRetryStrategy := boshretry.NewAttemptRetryStrategy(10, 1*time.Second, monitRetryable, logger) devicePathResolver := devicepathresolver.NewIdentityDevicePathResolver() routesSearcher := boshnet.NewCmdRoutesSearcher(runner) defaultNetworkResolver = boshnet.NewDefaultNetworkResolver(routesSearcher, ipResolver) state, err := boshplatform.NewBootstrapState(fs, "/tmp/agent_state.json") Expect(err).NotTo(HaveOccurred()) platform = boshplatform.NewLinuxPlatform( fs, runner, sigarCollector, compressor, copier, dirProvider, vitalsService, linuxCdutil, diskManager, ubuntuNetManager, ubuntuCertManager, monitRetryStrategy, devicePathResolver, 500*time.Millisecond, state, linuxOptions, logger, defaultNetworkResolver, ) }) JustBeforeEach(func() { settingsPath := filepath.Join("bosh", "settings.json") var settings boshsettings.Settings json.Unmarshal([]byte(settingsJSON), &settings) settingsSource := fakeinf.FakeSettingsSource{ PublicKey: "123", SettingsValue: settings, } settingsService := boshsettings.NewService( platform.GetFs(), settingsPath, settingsSource, platform, logger, ) boot = NewBootstrap( platform, dirProvider, settingsService, logger, ) }) Context("when a single network configuration is provided, with a MAC address", func() { BeforeEach(func() { settingsJSON = `{ "networks": { "netA": { "default": ["dns", "gateway"], "ip": "2.2.2.2", "dns": [ "8.8.8.8", "4.4.4.4" ], "netmask": "255.255.255.0", "gateway": "2.2.2.0", "mac": "aa:bb:cc" } } }` }) Context("and no physical network interfaces exist", func() { Context("and a single virtual network interface exists", func() { BeforeEach(func() { stubInterfaces([][]string{[]string{"lo", "aa:bb:cc", "virtual"}}) }) It("raises an error", func() { err := boot.Run() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Number of network settings '1' is greater than the number of network devices '0")) }) }) }) Context("and a single physical network interface exists", func() { BeforeEach(func() { stubInterfaces([][]string{[]string{"eth0", "aa:bb:cc", "physical"}}) interfaceAddrsProvider.GetInterfaceAddresses = []boship.InterfaceAddress{ boship.NewSimpleInterfaceAddress("eth0", "2.2.2.2"), } }) It("succeeds", func() { err := boot.Run() Expect(err).NotTo(HaveOccurred()) }) }) Context("and extra physical network interfaces exist", func() { BeforeEach(func() { stubInterfaces([][]string{[]string{"eth0", "aa:bb:cc", "physical"}, []string{"eth1", "aa:bb:dd", "physical"}}) interfaceAddrsProvider.GetInterfaceAddresses = []boship.InterfaceAddress{ boship.NewSimpleInterfaceAddress("eth0", "2.2.2.2"), } }) It("succeeds", func() { err := boot.Run() Expect(err).NotTo(HaveOccurred()) }) }) Context("and extra virtual network interfaces exist", func() { BeforeEach(func() { stubInterfaces([][]string{[]string{"eth0", "aa:bb:cc", "physical"}, []string{"lo", "aa:bb:ee", "virtual"}}) interfaceAddrsProvider.GetInterfaceAddresses = []boship.InterfaceAddress{ boship.NewSimpleInterfaceAddress("eth0", "2.2.2.2"), } }) It("succeeds", func() { err := boot.Run() Expect(err).ToNot(HaveOccurred()) }) }) }) Context("when a single network configuration is provided, without a MAC address", func() { BeforeEach(func() { settingsJSON = `{ "networks": { "netA": { "default": ["dns", "gateway"], "ip": "2.2.2.2", "dns": [ "8.8.8.8", "4.4.4.4" ], "netmask": "255.255.255.0", "gateway": "2.2.2.0" } } }` }) Context("and no physical network interfaces exist", func() { Context("and a single virtual network interface exists", func() { BeforeEach(func() { stubInterfaces([][]string{[]string{"lo", "aa:bb:cc", "virtual"}}) }) It("raises an error", func() { err := boot.Run() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Number of network settings '1' is greater than the number of network devices '0")) }) }) }) Context("and a single physical network interface exists", func() { BeforeEach(func() { stubInterfaces([][]string{[]string{"eth0", "aa:bb:cc", "physical"}}) interfaceAddrsProvider.GetInterfaceAddresses = []boship.InterfaceAddress{ boship.NewSimpleInterfaceAddress("eth0", "2.2.2.2"), } }) It("succeeds", func() { err := boot.Run() Expect(err).NotTo(HaveOccurred()) }) }) Context("and extra physical network interfaces exist", func() { BeforeEach(func() { stubInterfaces([][]string{[]string{"eth0", "aa:bb:cc", "physical"}, []string{"eth1", "aa:bb:dd", "physical"}}) interfaceAddrsProvider.GetInterfaceAddresses = []boship.InterfaceAddress{ boship.NewSimpleInterfaceAddress("eth0", "2.2.2.2"), } }) It("succeeds", func() { err := boot.Run() Expect(err).NotTo(HaveOccurred()) }) }) Context("and an extra virtual network interface exists", func() { BeforeEach(func() { stubInterfaces([][]string{[]string{"eth0", "aa:bb:cc", "physical"}, []string{"lo", "aa:bb:dd", "virtual"}}) interfaceAddrsProvider.GetInterfaceAddresses = []boship.InterfaceAddress{ boship.NewSimpleInterfaceAddress("eth0", "2.2.2.2"), } }) It("succeeds", func() { err := boot.Run() Expect(err).NotTo(HaveOccurred()) }) }) }) Context("when two network configurations are provided", func() { BeforeEach(func() { settingsJSON = `{ "networks": { "netA": { "default": ["dns", "gateway"], "ip": "2.2.2.2", "dns": [ "8.8.8.8", "4.4.4.4" ], "netmask": "255.255.255.0", "gateway": "2.2.2.0", "mac": "aa:bb:cc" }, "netB": { "default": ["dns", "gateway"], "ip": "3.3.3.3", "dns": [ "8.8.8.8", "4.4.4.4" ], "netmask": "255.255.255.0", "gateway": "3.3.3.0", "mac": "" } } }` }) Context("and a single physical network interface exists", func() { BeforeEach(func() { stubInterfaces([][]string{[]string{"eth0", "aa:bb:cc", "physical"}}) }) It("raises an error", func() { err := boot.Run() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Number of network settings '2' is greater than the number of network devices '1")) }) }) Context("and two physical network interfaces with matching MAC addresses exist", func() { BeforeEach(func() { stubInterfaces([][]string{[]string{"eth0", "aa:bb:cc", "physical"}, []string{"eth1", "aa:bb:dd", "physical"}}) interfaceAddrsProvider.GetInterfaceAddresses = []boship.InterfaceAddress{ boship.NewSimpleInterfaceAddress("eth0", "2.2.2.2"), boship.NewSimpleInterfaceAddress("eth1", "3.3.3.3"), } }) It("succeeds", func() { err := boot.Run() Expect(err).ToNot(HaveOccurred()) }) }) }) }) }) }
func init() { Describe("ApplyAction", func() { var ( applier *fakeappl.FakeApplier specService *fakeas.FakeV1Service settingsService *fakesettings.FakeSettingsService dirProvider boshdir.Provider action ApplyAction fs boshsys.FileSystem ) BeforeEach(func() { applier = fakeappl.NewFakeApplier() specService = fakeas.NewFakeV1Service() settingsService = &fakesettings.FakeSettingsService{} dirProvider = boshdir.NewProvider("/var/vcap") fs = fakesys.NewFakeFileSystem() action = NewApply(applier, specService, settingsService, dirProvider.InstanceDir(), fs) }) It("apply should be asynchronous", func() { Expect(action.IsAsynchronous()).To(BeTrue()) }) It("is not persistent", func() { Expect(action.IsPersistent()).To(BeFalse()) }) Describe("Run", func() { settings := boshsettings.Settings{AgentID: "fake-agent-id"} BeforeEach(func() { settingsService.Settings = settings }) Context("when desired spec has configuration hash", func() { currentApplySpec := boshas.V1ApplySpec{ConfigurationHash: "fake-current-config-hash"} desiredApplySpec := boshas.V1ApplySpec{ConfigurationHash: "fake-desired-config-hash"} populatedDesiredApplySpec := boshas.V1ApplySpec{ ConfigurationHash: "fake-populated-desired-config-hash", } Context("when current spec can be retrieved", func() { BeforeEach(func() { specService.Spec = currentApplySpec }) It("populates dynamic networks in desired spec", func() { _, err := action.Run(desiredApplySpec) Expect(err).ToNot(HaveOccurred()) Expect(specService.PopulateDHCPNetworksSpec).To(Equal(desiredApplySpec)) Expect(specService.PopulateDHCPNetworksSettings).To(Equal(settings)) }) Context("when resolving dynamic networks succeeds", func() { BeforeEach(func() { specService.PopulateDHCPNetworksResultSpec = populatedDesiredApplySpec }) It("runs applier with populated desired spec", func() { _, err := action.Run(desiredApplySpec) Expect(err).ToNot(HaveOccurred()) Expect(applier.Applied).To(BeTrue()) Expect(applier.ApplyCurrentApplySpec).To(Equal(currentApplySpec)) Expect(applier.ApplyDesiredApplySpec).To(Equal(populatedDesiredApplySpec)) }) Context("when applier succeeds applying desired spec", func() { Context("when saving desires spec as current spec succeeds", func() { It("returns 'applied' after setting populated desired spec as current spec", func() { value, err := action.Run(desiredApplySpec) Expect(err).ToNot(HaveOccurred()) Expect(value).To(Equal("applied")) Expect(specService.Spec).To(Equal(populatedDesiredApplySpec)) }) Context("desired spec has id, instance name, deployment name, and az", func() { BeforeEach(func() { desiredApplySpec = boshas.V1ApplySpec{ConfigurationHash: "fake-desired-config-hash", NodeID: "node-id01-123f-r2344", AvailabilityZone: "ex-az", Deployment: "deployment-name", Name: "instance-name"} specService.PopulateDHCPNetworksResultSpec = desiredApplySpec }) It("returns 'applied' and writes the id, instance name, deployment name, and az to files in the instance directory", func() { value, err := action.Run(desiredApplySpec) Expect(err).ToNot(HaveOccurred()) Expect(value).To(Equal("applied")) instanceDir := dirProvider.InstanceDir() id, err := fs.ReadFileString(path.Join(instanceDir, "id")) Expect(err).ToNot(HaveOccurred()) Expect(id).To(Equal(desiredApplySpec.NodeID)) az, err := fs.ReadFileString(path.Join(instanceDir, "az")) Expect(err).ToNot(HaveOccurred()) Expect(az).To(Equal(desiredApplySpec.AvailabilityZone)) instanceName, err := fs.ReadFileString(path.Join(instanceDir, "name")) Expect(err).ToNot(HaveOccurred()) Expect(instanceName).To(Equal(desiredApplySpec.Name)) deploymentName, err := fs.ReadFileString(path.Join(instanceDir, "deployment")) Expect(err).ToNot(HaveOccurred()) Expect(deploymentName).To(Equal(desiredApplySpec.Deployment)) }) }) }) Context("when saving populated desires spec as current spec fails", func() { It("returns error because agent was not able to remember that is converged to desired spec", func() { specService.SetErr = errors.New("fake-set-error") _, err := action.Run(desiredApplySpec) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-set-error")) }) }) }) Context("when applier fails applying desired spec", func() { BeforeEach(func() { applier.ApplyError = errors.New("fake-apply-error") }) It("returns error", func() { _, err := action.Run(desiredApplySpec) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-apply-error")) }) It("does not save desired spec as current spec", func() { _, err := action.Run(desiredApplySpec) Expect(err).To(HaveOccurred()) Expect(specService.Spec).To(Equal(currentApplySpec)) }) }) }) Context("when resolving dynamic networks fails", func() { BeforeEach(func() { specService.PopulateDHCPNetworksErr = errors.New("fake-populate-dynamic-networks-err") }) It("returns error", func() { _, err := action.Run(desiredApplySpec) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-populate-dynamic-networks-err")) }) It("does not apply desired spec as current spec", func() { _, err := action.Run(desiredApplySpec) Expect(err).To(HaveOccurred()) Expect(applier.Applied).To(BeFalse()) }) It("does not save desired spec as current spec", func() { _, err := action.Run(desiredApplySpec) Expect(err).To(HaveOccurred()) Expect(specService.Spec).To(Equal(currentApplySpec)) }) }) }) Context("when current spec cannot be retrieved", func() { BeforeEach(func() { specService.Spec = currentApplySpec specService.GetErr = errors.New("fake-get-error") }) It("returns error and does not apply desired spec", func() { _, err := action.Run(desiredApplySpec) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-get-error")) }) It("does not run applier with desired spec", func() { _, err := action.Run(desiredApplySpec) Expect(err).To(HaveOccurred()) Expect(applier.Applied).To(BeFalse()) }) It("does not save desired spec as current spec", func() { _, err := action.Run(desiredApplySpec) Expect(err).To(HaveOccurred()) Expect(specService.Spec).To(Equal(currentApplySpec)) }) }) }) Context("when desired spec does not have a configuration hash", func() { desiredApplySpec := boshas.V1ApplySpec{ JobSpec: boshas.JobSpec{ Template: "fake-job-template", }, } populatedDesiredApplySpec := boshas.V1ApplySpec{ JobSpec: boshas.JobSpec{ Template: "fake-populated-job-template", }, } It("populates dynamic networks in desired spec", func() { _, err := action.Run(desiredApplySpec) Expect(err).ToNot(HaveOccurred()) Expect(specService.PopulateDHCPNetworksSpec).To(Equal(desiredApplySpec)) Expect(specService.PopulateDHCPNetworksSettings).To(Equal(settings)) }) Context("when resolving dynamic networks succeeds", func() { BeforeEach(func() { specService.PopulateDHCPNetworksResultSpec = populatedDesiredApplySpec }) Context("when saving desires spec as current spec succeeds", func() { It("returns 'applied' after setting desired spec as current spec", func() { value, err := action.Run(desiredApplySpec) Expect(err).ToNot(HaveOccurred()) Expect(value).To(Equal("applied")) Expect(specService.Spec).To(Equal(populatedDesiredApplySpec)) }) It("does not try to apply desired spec since it does not have jobs and packages", func() { _, err := action.Run(desiredApplySpec) Expect(err).ToNot(HaveOccurred()) Expect(applier.Applied).To(BeFalse()) }) }) Context("when saving desires spec as current spec fails", func() { BeforeEach(func() { specService.SetErr = errors.New("fake-set-error") }) It("returns error because agent was not able to remember that is converged to desired spec", func() { _, err := action.Run(desiredApplySpec) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-set-error")) }) It("does not try to apply desired spec since it does not have jobs and packages", func() { _, err := action.Run(desiredApplySpec) Expect(err).To(HaveOccurred()) Expect(applier.Applied).To(BeFalse()) }) }) }) Context("when resolving dynamic networks fails", func() { BeforeEach(func() { specService.PopulateDHCPNetworksErr = errors.New("fake-populate-dynamic-networks-err") }) It("returns error", func() { _, err := action.Run(desiredApplySpec) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-populate-dynamic-networks-err")) }) It("does not apply desired spec as current spec", func() { _, err := action.Run(desiredApplySpec) Expect(err).To(HaveOccurred()) Expect(applier.Applied).To(BeFalse()) }) It("does not save desired spec as current spec", func() { _, err := action.Run(desiredApplySpec) Expect(err).To(HaveOccurred()) Expect(specService.Spec).ToNot(Equal(desiredApplySpec)) }) }) }) }) }) }
func init() { Describe("App", func() { var ( baseDir string agentConfPath string agentConfJSON string app App ) BeforeEach(func() { var err error baseDir, err = ioutil.TempDir("", "go-agent-test") Expect(err).ToNot(HaveOccurred()) err = os.Mkdir(filepath.Join(baseDir, "bosh"), os.ModePerm) Expect(err).ToNot(HaveOccurred()) }) BeforeEach(func() { agentConfPath = filepath.Join(baseDir, "bosh", "agent.json") agentConfJSON = `{ "Infrastructure": { "Settings": { "Sources": [{ "Type": "CDROM", "FileName": "/fake-file-name" }] } } }` settingsPath := filepath.Join(baseDir, "bosh", "settings.json") settingsJSON := `{ "agent_id": "my-agent-id", "blobstore": { "options": { "bucket_name": "george", "encryption_key": "optional encryption key", "access_key_id": "optional access key id", "secret_access_key": "optional secret access key" }, "provider": "dummy" }, "disks": { "ephemeral": "/dev/sdb", "persistent": { "vol-xxxxxx": "/dev/sdf" }, "system": "/dev/sda1" }, "env": { "bosh": { "password": "******" } }, "networks": { "netA": { "default": ["dns", "gateway"], "ip": "ww.ww.ww.ww", "dns": [ "xx.xx.xx.xx", "yy.yy.yy.yy" ] }, "netB": { "dns": [ "zz.zz.zz.zz" ] } }, "Mbus": "https://*****:*****@0.0.0.0:6868", "ntp": [ "0.north-america.pool.ntp.org", "1.north-america.pool.ntp.org" ], "vm": { "name": "vm-abc-def" } }` err := ioutil.WriteFile(settingsPath, []byte(settingsJSON), 0640) Expect(err).ToNot(HaveOccurred()) }) JustBeforeEach(func() { err := ioutil.WriteFile(agentConfPath, []byte(agentConfJSON), 0640) Expect(err).ToNot(HaveOccurred()) logger := boshlog.NewLogger(boshlog.LevelNone) fakefs := boshsys.NewOsFileSystem(logger) app = New(logger, fakefs) }) AfterEach(func() { os.RemoveAll(baseDir) }) It("Sets up device path resolver on platform specific to infrastructure", func() { err := app.Setup([]string{"bosh-agent", "-P", "dummy", "-C", agentConfPath, "-b", baseDir}) Expect(err).ToNot(HaveOccurred()) Expect(app.GetPlatform().GetDevicePathResolver()).To(Equal(devicepathresolver.NewIdentityDevicePathResolver())) }) Context("when DevicePathResolutionType is 'virtio'", func() { BeforeEach(func() { agentConfJSON = `{ "Platform": { "Linux": { "DevicePathResolutionType": "virtio" } }, "Infrastructure": { "Settings": { "Sources": [{ "Type": "CDROM", "FileName": "/fake-file-name" }] } } }` }) It("uses a VirtioDevicePathResolver", func() { err := app.Setup([]string{"bosh-agent", "-P", "dummy", "-C", agentConfPath, "-b", baseDir}) Expect(err).ToNot(HaveOccurred()) logLevel, err := boshlog.Levelify("DEBUG") Expect(err).NotTo(HaveOccurred()) Expect(app.GetPlatform().GetDevicePathResolver()).To( BeAssignableToTypeOf(devicepathresolver.NewVirtioDevicePathResolver(nil, nil, boshlog.NewLogger(logLevel)))) }) }) Context("when DevicePathResolutionType is 'scsi'", func() { BeforeEach(func() { agentConfJSON = `{ "Platform": { "Linux": { "DevicePathResolutionType": "scsi" } }, "Infrastructure": { "Settings": { "Sources": [{ "Type": "CDROM", "FileName": "/fake-file-name" }] } } }` }) It("uses a VirtioDevicePathResolver", func() { err := app.Setup([]string{"bosh-agent", "-P", "dummy", "-C", agentConfPath, "-b", baseDir}) Expect(err).ToNot(HaveOccurred()) Expect(app.GetPlatform().GetDevicePathResolver()).To( BeAssignableToTypeOf(devicepathresolver.NewScsiDevicePathResolver(nil, nil, nil))) }) }) Context("logging stemcell version and git sha", func() { var ( logger boshlog.Logger outBuf *bytes.Buffer fakeFs boshsys.FileSystem stemcellVersionFilePath string stemcellSha1FilePath string ) JustBeforeEach(func() { outBuf = bytes.NewBufferString("") errBuf := bytes.NewBufferString("") logger = boshlog.NewWriterLogger(boshlog.LevelDebug, outBuf, errBuf) fakeFs = fakesys.NewFakeFileSystem() dirProvider := boshdirs.NewProvider(baseDir) stemcellVersionFilePath = filepath.Join(dirProvider.EtcDir(), "stemcell_version") stemcellSha1FilePath = filepath.Join(dirProvider.EtcDir(), "stemcell_git_sha1") app = New(logger, fakeFs) }) Context("when stemcell version and sha files are present", func() { It("should print out the stemcell version and sha in the logs", func() { fakeFs.WriteFileString(stemcellVersionFilePath, "version-blah") fakeFs.WriteFileString(stemcellSha1FilePath, "sha1-blah") app.Setup([]string{"bosh-agent", "-P", "dummy", "-C", agentConfPath, "-b", baseDir}) Expect(string(outBuf.Bytes())).To(ContainSubstring("Running on stemcell version 'version-blah' (git: sha1-blah)")) }) }) Context("when stemcell version file is NOT present", func() { It("should print out the sha in the logs", func() { fakeFs.WriteFileString(stemcellSha1FilePath, "sha1-blah") app.Setup([]string{"bosh-agent", "-P", "dummy", "-C", agentConfPath, "-b", baseDir}) Expect(string(outBuf.Bytes())).To(ContainSubstring("Running on stemcell version '?' (git: sha1-blah)")) }) }) Context("when sha version file is NOT present", func() { It("should print out the stemcell version in the logs", func() { fakeFs.WriteFileString(stemcellVersionFilePath, "version-blah") app.Setup([]string{"bosh-agent", "-P", "dummy", "-C", agentConfPath, "-b", baseDir}) Expect(string(outBuf.Bytes())).To(ContainSubstring("Running on stemcell version 'version-blah' (git: ?)")) }) }) Context("when stemcell version file is empty", func() { It("should print out the sha in the logs", func() { fakeFs.WriteFileString(stemcellVersionFilePath, "") fakeFs.WriteFileString(stemcellSha1FilePath, "sha1-blah") app.Setup([]string{"bosh-agent", "-P", "dummy", "-C", agentConfPath, "-b", baseDir}) Expect(string(outBuf.Bytes())).To(ContainSubstring("Running on stemcell version '?' (git: sha1-blah)")) }) }) Context("when sha version file is empty", func() { It("should print out the stemcell version in the logs", func() { fakeFs.WriteFileString(stemcellVersionFilePath, "version-blah") fakeFs.WriteFileString(stemcellSha1FilePath, "") app.Setup([]string{"bosh-agent", "-P", "dummy", "-C", agentConfPath, "-b", baseDir}) Expect(string(outBuf.Bytes())).To(ContainSubstring("Running on stemcell version 'version-blah' (git: ?)")) }) }) Context("when stemcell version and sha files are NOT present", func() { It("should print unknown version and sha in the logs", func() { app.Setup([]string{"bosh-agent", "-P", "dummy", "-C", agentConfPath, "-b", baseDir}) Expect(string(outBuf.Bytes())).To(ContainSubstring("Running on stemcell version '?' (git: ?)")) }) }) }) }) }
func init() { Describe("settingsService", func() { var ( fs *fakesys.FakeFileSystem fakeDefaultNetworkResolver *fakenet.FakeDefaultNetworkResolver fakeSettingsSource *fakes.FakeSettingsSource ) BeforeEach(func() { fs = fakesys.NewFakeFileSystem() fakeDefaultNetworkResolver = &fakenet.FakeDefaultNetworkResolver{} fakeSettingsSource = &fakes.FakeSettingsSource{} }) buildService := func() (Service, *fakesys.FakeFileSystem) { logger := boshlog.NewLogger(boshlog.LevelNone) service := NewService(fs, "/setting/path.json", fakeSettingsSource, fakeDefaultNetworkResolver, logger) return service, fs } Describe("LoadSettings", func() { var ( fetchedSettings Settings fetcherFuncErr error service Service ) BeforeEach(func() { fetchedSettings = Settings{} fetcherFuncErr = nil }) JustBeforeEach(func() { fakeSettingsSource.SettingsValue = fetchedSettings fakeSettingsSource.SettingsErr = fetcherFuncErr service, fs = buildService() }) Context("when settings fetcher succeeds fetching settings", func() { BeforeEach(func() { fetchedSettings = Settings{AgentID: "some-new-agent-id"} }) Context("when settings contain at most one dynamic network", func() { BeforeEach(func() { fetchedSettings.Networks = Networks{ "fake-net-1": Network{Type: NetworkTypeDynamic}, } }) It("updates the service with settings from the fetcher", func() { err := service.LoadSettings() Expect(err).NotTo(HaveOccurred()) Expect(service.GetSettings().AgentID).To(Equal("some-new-agent-id")) }) It("persists settings to the settings file", func() { err := service.LoadSettings() Expect(err).NotTo(HaveOccurred()) json, err := json.Marshal(fetchedSettings) Expect(err).NotTo(HaveOccurred()) fileContent, err := fs.ReadFile("/setting/path.json") Expect(err).NotTo(HaveOccurred()) Expect(fileContent).To(Equal(json)) }) It("returns any error from writing to the setting file", func() { fs.WriteFileError = errors.New("fs-write-file-error") err := service.LoadSettings() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fs-write-file-error")) }) }) }) Context("when settings fetcher fails fetching settings", func() { BeforeEach(func() { fetcherFuncErr = errors.New("fake-fetch-error") }) Context("when a settings file exists", func() { Context("when settings contain at most one dynamic network", func() { BeforeEach(func() { fs.WriteFile("/setting/path.json", []byte(`{ "agent_id":"some-agent-id", "networks": {"fake-net-1": {"type": "dynamic"}} }`)) fakeDefaultNetworkResolver.GetDefaultNetworkNetwork = Network{ IP: "fake-resolved-ip", Netmask: "fake-resolved-netmask", Gateway: "fake-resolved-gateway", } }) It("returns settings from the settings file with resolved network", func() { err := service.LoadSettings() Expect(err).ToNot(HaveOccurred()) Expect(service.GetSettings()).To(Equal(Settings{ AgentID: "some-agent-id", Networks: Networks{ "fake-net-1": Network{ Type: NetworkTypeDynamic, IP: "fake-resolved-ip", Netmask: "fake-resolved-netmask", Gateway: "fake-resolved-gateway", Resolved: true, }, }, })) }) }) }) Context("when non-unmarshallable settings file exists", func() { It("returns any error from the fetcher", func() { fs.WriteFile("/setting/path.json", []byte(`$%^&*(`)) err := service.LoadSettings() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-fetch-error")) Expect(service.GetSettings()).To(Equal(Settings{})) }) }) Context("when no settings file exists", func() { It("returns any error from the fetcher", func() { err := service.LoadSettings() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-fetch-error")) Expect(service.GetSettings()).To(Equal(Settings{})) }) }) }) }) Describe("InvalidateSettings", func() { It("removes the settings file", func() { fakeSettingsSource.SettingsValue = Settings{} fakeSettingsSource.SettingsErr = nil service, fs := buildService() fs.WriteFile("/setting/path.json", []byte(`{}`)) err := service.InvalidateSettings() Expect(err).ToNot(HaveOccurred()) Expect(fs.FileExists("/setting/path.json")).To(BeFalse()) }) It("returns err if removing settings file errored", func() { fakeSettingsSource.SettingsValue = Settings{} fakeSettingsSource.SettingsErr = nil service, fs := buildService() fs.RemoveAllError = errors.New("fs-remove-all-error") err := service.InvalidateSettings() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fs-remove-all-error")) }) }) Describe("GetSettings", func() { var ( loadedSettings Settings service Service ) BeforeEach(func() { loadedSettings = Settings{AgentID: "some-agent-id"} }) JustBeforeEach(func() { fakeSettingsSource.SettingsValue = loadedSettings fakeSettingsSource.SettingsErr = nil service, _ = buildService() err := service.LoadSettings() Expect(err).NotTo(HaveOccurred()) }) Context("when there is are no dynamic networks", func() { It("returns settings without modifying any networks", func() { Expect(service.GetSettings()).To(Equal(loadedSettings)) }) It("does not try to determine default network", func() { _ = service.GetSettings() Expect(fakeDefaultNetworkResolver.GetDefaultNetworkCalled).To(BeFalse()) }) }) Context("when there is network that needs to be resolved (ip, netmask, or mac are not set)", func() { BeforeEach(func() { loadedSettings = Settings{ Networks: map[string]Network{ "fake-net1": Network{ IP: "fake-net1-ip", Netmask: "fake-net1-netmask", Mac: "fake-net1-mac", Gateway: "fake-net1-gateway", }, "fake-net2": Network{ Gateway: "fake-net2-gateway", DNS: []string{"fake-net2-dns"}, }, }, } }) Context("when default network can be retrieved", func() { BeforeEach(func() { fakeDefaultNetworkResolver.GetDefaultNetworkNetwork = Network{ IP: "fake-resolved-ip", Netmask: "fake-resolved-netmask", Gateway: "fake-resolved-gateway", } }) It("returns settings with resolved dynamic network ip, netmask, gateway and keeping everything else the same", func() { settings := service.GetSettings() Expect(settings).To(Equal(Settings{ Networks: map[string]Network{ "fake-net1": Network{ IP: "fake-net1-ip", Netmask: "fake-net1-netmask", Mac: "fake-net1-mac", Gateway: "fake-net1-gateway", }, "fake-net2": Network{ IP: "fake-resolved-ip", Netmask: "fake-resolved-netmask", Gateway: "fake-resolved-gateway", DNS: []string{"fake-net2-dns"}, Resolved: true, }, }, })) }) }) Context("when default network fails to be retrieved", func() { BeforeEach(func() { fakeDefaultNetworkResolver.GetDefaultNetworkErr = errors.New("fake-get-default-network-err") }) It("returns error", func() { settings := service.GetSettings() Expect(settings).To(Equal(loadedSettings)) }) }) }) }) }) }
func rootDesc() { var ( repo ReleaseRepo deploymentStateService DeploymentStateService fs *fakesys.FakeFileSystem fakeUUIDGenerator *fakeuuid.FakeGenerator ) BeforeEach(func() { logger := boshlog.NewLogger(boshlog.LevelNone) fs = fakesys.NewFakeFileSystem() fakeUUIDGenerator = &fakeuuid.FakeGenerator{} fakeUUIDGenerator.GeneratedUUID = "fake-uuid" deploymentStateService = NewFileSystemDeploymentStateService(fs, fakeUUIDGenerator, logger, "/fake/path") deploymentStateService.Load() repo = NewReleaseRepo(deploymentStateService, fakeUUIDGenerator) }) Describe("List", func() { Context("when a current release exists", func() { BeforeEach(func() { conf, err := deploymentStateService.Load() Expect(err).ToNot(HaveOccurred()) conf.Releases = []ReleaseRecord{ ReleaseRecord{ID: "fake-guid-a", Name: "fake-name-a", Version: "fake-version-a"}, ReleaseRecord{ID: "fake-guid-b", Name: "fake-name-b", Version: "fake-version-b"}, } err = deploymentStateService.Save(conf) Expect(err).ToNot(HaveOccurred()) }) It("returns existing release", func() { records, err := repo.List() Expect(err).ToNot(HaveOccurred()) Expect(records).To(Equal([]ReleaseRecord{ { ID: "fake-guid-a", Name: "fake-name-a", Version: "fake-version-a", }, { ID: "fake-guid-b", Name: "fake-name-b", Version: "fake-version-b", }, })) }) }) Context("when there are no releases recorded", func() { It("returns not found", func() { records, err := repo.List() Expect(err).ToNot(HaveOccurred()) Expect(records).To(HaveLen(0)) }) }) Context("when the config service fails to load", func() { BeforeEach(func() { fs.ReadFileError = errors.New("kaboom") }) It("returns an error", func() { _, err := repo.List() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Loading existing config")) }) }) }) Describe("Update", func() { Context("when there are no existing releases", func() { It("saves the provided releases to the config file", func() { err := repo.Update([]release.Release{ fakerelease.New("name1", "1"), fakerelease.New("name2", "2"), }) Expect(err).ToNot(HaveOccurred()) conf, err := deploymentStateService.Load() Expect(err).ToNot(HaveOccurred()) Expect(conf.Releases).To(ConsistOf( ReleaseRecord{ID: "fake-uuid", Name: "name1", Version: "1"}, ReleaseRecord{ID: "fake-uuid", Name: "name2", Version: "2"}, )) }) }) Context("when the existing releases exactly match the provided releases", func() { BeforeEach(func() { conf, err := deploymentStateService.Load() Expect(err).ToNot(HaveOccurred()) conf.Releases = []ReleaseRecord{ ReleaseRecord{ID: "old-uuid", Name: "name1", Version: "1"}, ReleaseRecord{ID: "old-uuid", Name: "name2", Version: "2"}, } err = deploymentStateService.Save(conf) Expect(err).ToNot(HaveOccurred()) }) }) Context("when existing versions differ from the provided release versions", func() { BeforeEach(func() { conf, err := deploymentStateService.Load() Expect(err).ToNot(HaveOccurred()) conf.Releases = []ReleaseRecord{ ReleaseRecord{ID: "old-uuid", Name: "name1", Version: "1"}, ReleaseRecord{ID: "old-uuid", Name: "name2", Version: "3"}, } err = deploymentStateService.Save(conf) Expect(err).ToNot(HaveOccurred()) }) It("saves the provided releases to the config file", func() { err := repo.Update([]release.Release{ fakerelease.New("name1", "1"), fakerelease.New("name2", "2"), }) Expect(err).ToNot(HaveOccurred()) conf, err := deploymentStateService.Load() Expect(err).ToNot(HaveOccurred()) Expect(conf.Releases).To(ConsistOf( ReleaseRecord{ID: "fake-uuid", Name: "name1", Version: "1"}, ReleaseRecord{ID: "fake-uuid", Name: "name2", Version: "2"}, )) }) }) Context("when existing names differ from the provided release names", func() { BeforeEach(func() { conf, err := deploymentStateService.Load() Expect(err).ToNot(HaveOccurred()) conf.Releases = []ReleaseRecord{ ReleaseRecord{ID: "old-uuid", Name: "name1", Version: "1"}, ReleaseRecord{ID: "old-uuid", Name: "other-name", Version: "2"}, } err = deploymentStateService.Save(conf) Expect(err).ToNot(HaveOccurred()) }) It("saves the provided releases to the config file", func() { err := repo.Update([]release.Release{ fakerelease.New("name1", "1"), fakerelease.New("name2", "2"), }) Expect(err).ToNot(HaveOccurred()) conf, err := deploymentStateService.Load() Expect(err).ToNot(HaveOccurred()) Expect(conf.Releases).To(ConsistOf( ReleaseRecord{ID: "fake-uuid", Name: "name1", Version: "1"}, ReleaseRecord{ID: "fake-uuid", Name: "name2", Version: "2"}, )) }) }) Context("when a release is removed", func() { BeforeEach(func() { conf, err := deploymentStateService.Load() Expect(err).ToNot(HaveOccurred()) conf.Releases = []ReleaseRecord{ ReleaseRecord{ID: "old-uuid", Name: "name1", Version: "1"}, ReleaseRecord{ID: "old-uuid", Name: "name2", Version: "2"}, ReleaseRecord{ID: "old-uuid", Name: "name3", Version: "3"}, } err = deploymentStateService.Save(conf) Expect(err).ToNot(HaveOccurred()) }) It("saves the provided releases to the config file", func() { err := repo.Update([]release.Release{ fakerelease.New("name1", "1"), fakerelease.New("name2", "2"), }) Expect(err).ToNot(HaveOccurred()) conf, err := deploymentStateService.Load() Expect(err).ToNot(HaveOccurred()) Expect(conf.Releases).To(ConsistOf( ReleaseRecord{ID: "fake-uuid", Name: "name1", Version: "1"}, ReleaseRecord{ID: "fake-uuid", Name: "name2", Version: "2"}, )) }) }) Context("when a release is added", func() { BeforeEach(func() { conf, err := deploymentStateService.Load() Expect(err).ToNot(HaveOccurred()) conf.Releases = []ReleaseRecord{ ReleaseRecord{ID: "old-uuid", Name: "name1", Version: "1"}, } err = deploymentStateService.Save(conf) Expect(err).ToNot(HaveOccurred()) }) It("saves the provided releases to the config file", func() { err := repo.Update([]release.Release{ fakerelease.New("name1", "1"), fakerelease.New("name2", "2"), }) Expect(err).ToNot(HaveOccurred()) conf, err := deploymentStateService.Load() Expect(err).ToNot(HaveOccurred()) Expect(conf.Releases).To(ConsistOf( ReleaseRecord{ID: "fake-uuid", Name: "name1", Version: "1"}, ReleaseRecord{ID: "fake-uuid", Name: "name2", Version: "2"}, )) }) }) Context("when the config service fails to save", func() { BeforeEach(func() { fs.WriteFileError = errors.New("kaboom") }) It("returns an error", func() { err := repo.Update([]release.Release{ fakerelease.New("name1", "1"), fakerelease.New("name2", "2"), }) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("kaboom")) }) }) }) }
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(boshcrypto.NewDigest("sha1", "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("can process sha1 checksums in the new format", func() { blobstore.GetFileName = "/fake-blobstore-file-name" job.Source.Sha1 = "sha1:fake-blob-sha1" err := act() Expect(err).ToNot(HaveOccurred()) Expect(blobstore.GetBlobIDs[0]).To(Equal("fake-blobstore-id")) Expect(blobstore.GetFingerprints[0]).To(Equal(boshcrypto.NewDigest("sha1", "fake-blob-sha1"))) }) It("can process sha2 checksums", func() { blobstore.GetFileName = "/fake-blobstore-file-name" job.Source.Sha1 = "sha256:fake-blob-sha256" err := act() Expect(err).ToNot(HaveOccurred()) Expect(blobstore.GetBlobIDs[0]).To(Equal("fake-blobstore-id")) Expect(blobstore.GetFingerprints[0]).To(Equal(boshcrypto.NewDigest("sha256", "fake-blob-sha256"))) }) It("returns error when given and unsupported fingerprint", func() { blobstore.GetFileName = "/fake-blobstore-file-name" job.Source.Sha1 = "unsupported:checksum" err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Parsing job blob digest")) }) 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")) }) }) }) }
func init() { Describe("execCmdRunner", func() { var ( runner CmdRunner ) BeforeEach(func() { runner = NewExecCmdRunner(boshlog.NewLogger(boshlog.LevelNone)) }) Describe("RunComplexCommand", func() { It("run complex command with working directory", func() { cmd := Command{ Name: "ls", Args: []string{"-l"}, WorkingDir: ".", } stdout, stderr, status, err := runner.RunComplexCommand(cmd) Expect(err).ToNot(HaveOccurred()) Expect(stdout).To(ContainSubstring("exec_cmd_runner_fixtures")) Expect(stdout).To(ContainSubstring("total")) Expect(stderr).To(BeEmpty()) Expect(status).To(Equal(0)) }) It("run complex command with env", func() { cmd := Command{ Name: "env", Env: map[string]string{ "FOO": "BAR", }, } stdout, stderr, status, err := runner.RunComplexCommand(cmd) Expect(err).ToNot(HaveOccurred()) Expect(stdout).To(ContainSubstring("FOO=BAR")) Expect(stdout).To(ContainSubstring("PATH=")) Expect(stderr).To(BeEmpty()) Expect(status).To(Equal(0)) }) It("runs complex command with specific env", func() { cmd := Command{ Name: "env", Env: map[string]string{ "FOO": "BAR", }, UseIsolatedEnv: true, } stdout, stderr, status, err := runner.RunComplexCommand(cmd) Expect(err).ToNot(HaveOccurred()) Expect(stdout).To(ContainSubstring("FOO=BAR")) Expect(stdout).ToNot(ContainSubstring("PATH=")) Expect(stderr).To(BeEmpty()) Expect(status).To(Equal(0)) }) It("run complex command with stdin", func() { input := "This is STDIN\nWith another line." cmd := Command{ Name: "cat", Args: []string{"/dev/stdin"}, Stdin: strings.NewReader(input), } stdout, stderr, status, err := runner.RunComplexCommand(cmd) Expect(err).ToNot(HaveOccurred()) Expect(stdout).To(Equal(input)) Expect(stderr).To(BeEmpty()) Expect(status).To(Equal(0)) }) It("prints stdout/stderr to provided I/O object", func() { fs := fakesys.NewFakeFileSystem() stdoutFile, err := fs.OpenFile("/fake-stdout-path", os.O_RDWR, os.FileMode(0644)) Expect(err).ToNot(HaveOccurred()) stderrFile, err := fs.OpenFile("/fake-stderr-path", os.O_RDWR, os.FileMode(0644)) Expect(err).ToNot(HaveOccurred()) cmd := Command{ Name: "bash", Args: []string{"-c", "echo fake-out >&1; echo fake-err >&2"}, Stdout: stdoutFile, Stderr: stderrFile, } stdout, stderr, status, err := runner.RunComplexCommand(cmd) Expect(err).ToNot(HaveOccurred()) Expect(stdout).To(BeEmpty()) Expect(stderr).To(BeEmpty()) Expect(status).To(Equal(0)) stdoutContents := make([]byte, 1024) _, err = stdoutFile.Read(stdoutContents) Expect(err).ToNot(HaveOccurred()) Expect(string(stdoutContents)).To(ContainSubstring("fake-out")) stderrContents := make([]byte, 1024) _, err = stderrFile.Read(stderrContents) Expect(err).ToNot(HaveOccurred()) Expect(string(stderrContents)).To(ContainSubstring("fake-err")) }) }) Describe("RunComplexCommandAsync", func() { It("populates stdout and stderr", func() { cmd := Command{Name: "ls"} process, err := runner.RunComplexCommandAsync(cmd) Expect(err).ToNot(HaveOccurred()) result := <-process.Wait() Expect(result.Error).ToNot(HaveOccurred()) Expect(result.ExitStatus).To(Equal(0)) }) It("populates stdout and stderr", func() { cmd := Command{Name: "bash", Args: []string{"-c", "echo stdout >&1; echo stderr >&2"}} process, err := runner.RunComplexCommandAsync(cmd) Expect(err).ToNot(HaveOccurred()) result := <-process.Wait() Expect(result.Error).ToNot(HaveOccurred()) Expect(result.Stdout).To(Equal("stdout\n")) Expect(result.Stderr).To(Equal("stderr\n")) }) It("returns error and sets status to exit status of comamnd if it command exits with non-0 status", func() { cmd := Command{Name: "bash", Args: []string{"-c", "exit 10"}} process, err := runner.RunComplexCommandAsync(cmd) Expect(err).ToNot(HaveOccurred()) result := <-process.Wait() Expect(result.Error).To(HaveOccurred()) Expect(result.ExitStatus).To(Equal(10)) }) It("allows setting custom env variable in addition to inheriting process env variables", func() { cmd := Command{ Name: "env", Env: map[string]string{ "FOO": "BAR", }, } process, err := runner.RunComplexCommandAsync(cmd) Expect(err).ToNot(HaveOccurred()) result := <-process.Wait() Expect(result.Error).ToNot(HaveOccurred()) Expect(result.Stdout).To(ContainSubstring("FOO=BAR")) Expect(result.Stdout).To(ContainSubstring("PATH=")) }) It("changes working dir", func() { cmd := Command{Name: "bash", Args: []string{"-c", "echo $PWD"}, WorkingDir: "/tmp"} process, err := runner.RunComplexCommandAsync(cmd) Expect(err).ToNot(HaveOccurred()) result := <-process.Wait() Expect(result.Error).ToNot(HaveOccurred()) Expect(result.Stdout).To(ContainSubstring("/tmp")) }) }) Describe("RunCommand", func() { It("run command", func() { stdout, stderr, status, err := runner.RunCommand("echo", "Hello World!") Expect(err).ToNot(HaveOccurred()) Expect(stdout).To(Equal("Hello World!\n")) Expect(stderr).To(BeEmpty()) Expect(status).To(Equal(0)) }) It("run command with error output", func() { stdout, stderr, status, err := runner.RunCommand("bash", "-c", "echo error-output >&2") Expect(err).ToNot(HaveOccurred()) Expect(stdout).To(BeEmpty()) Expect(stderr).To(ContainSubstring("error-output")) Expect(status).To(Equal(0)) }) It("run command with non-0 exit status", func() { stdout, stderr, status, err := runner.RunCommand("bash", "-c", "exit 14") Expect(err).To(HaveOccurred()) Expect(stdout).To(BeEmpty()) Expect(stderr).To(BeEmpty()) Expect(status).To(Equal(14)) }) It("run command with error", func() { stdout, stderr, status, err := runner.RunCommand("false") Expect(err).To(HaveOccurred()) Expect(err.Error()).To(Equal("Running command: 'false', stdout: '', stderr: '': exit status 1")) Expect(stderr).To(BeEmpty()) Expect(stdout).To(BeEmpty()) Expect(status).To(Equal(1)) }) It("run command with error with args", func() { stdout, stderr, status, err := runner.RunCommand("false", "second arg") Expect(err).To(HaveOccurred()) Expect(err.Error()).To(Equal("Running command: 'false second arg', stdout: '', stderr: '': exit status 1")) Expect(stderr).To(BeEmpty()) Expect(stdout).To(BeEmpty()) Expect(status).To(Equal(1)) }) It("run command with cmd not found", func() { stdout, stderr, status, err := runner.RunCommand("something that does not exist") Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("not found")) Expect(stderr).To(BeEmpty()) Expect(stdout).To(BeEmpty()) Expect(status).To(Equal(-1)) }) }) Describe("CommandExists", func() { It("run command with input", func() { stdout, stderr, status, err := runner.RunCommandWithInput("foo\nbar\nbaz", "grep", "ba") Expect(err).ToNot(HaveOccurred()) Expect(stdout).To(Equal("bar\nbaz\n")) Expect(stderr).To(BeEmpty()) Expect(status).To(Equal(0)) }) }) Describe("CommandExists", func() { It("command exists", func() { Expect(runner.CommandExists("env")).To(BeTrue()) Expect(runner.CommandExists("absolutely-does-not-exist-ever-please-unicorns")).To(BeFalse()) }) }) }) Describe("execProcess", func() { var ( runner CmdRunner ) BeforeEach(func() { runner = NewExecCmdRunner(boshlog.NewLogger(boshlog.LevelNone)) }) Describe("TerminateNicely", func() { var ( buildDir string ) hasProcessesFromBuildDir := func() (bool, string) { // Make sure to show all processes on the system output, err := exec.Command("ps", "-A", "-o", "pid,args").Output() Expect(err).ToNot(HaveOccurred()) // Cannot check for PID existence directly because // PID could have been recycled by the OS; make sure it's not the same process for _, line := range strings.Split(strings.TrimSpace(string(output)), "\n") { if strings.Contains(line, buildDir) { return true, line } } return false, "" } expectProcessesToNotExist := func() { exists, ps := hasProcessesFromBuildDir() Expect(exists).To(BeFalse(), "Expected following process to not exist %s", ps) } BeforeEach(func() { var ( err error ) buildDir, err = ioutil.TempDir("", "TerminateNicely") Expect(err).ToNot(HaveOccurred()) exesToCompile := []string{ "exe_exits", "child_ignore_term", "child_term", "parent_ignore_term", "parent_term", } for _, exe := range exesToCompile { dst := filepath.Join(buildDir, exe) src := filepath.Join("exec_cmd_runner_fixtures", exe+".go") err := exec.Command("go", "build", "-o", dst, src).Run() Expect(err).ToNot(HaveOccurred()) } }) AfterEach(func() { os.RemoveAll(buildDir) }) Context("when parent and child terminate after receiving SIGTERM", func() { It("sends term signal to the whole group and returns with exit status that parent exited", func() { cmd := Command{Name: filepath.Join(buildDir, "parent_term")} process, err := runner.RunComplexCommandAsync(cmd) Expect(err).ToNot(HaveOccurred()) // Wait for script to start and output pids time.Sleep(2 * time.Second) waitCh := process.Wait() err = process.TerminateNicely(1 * time.Minute) Expect(err).ToNot(HaveOccurred()) result := <-waitCh Expect(result.Error).To(HaveOccurred()) // Parent exit code is returned // bash adds 128 to signal status as exit code Expect(result.ExitStatus).To(Equal(13)) // Term signal was sent to all processes in the group Expect(result.Stdout).To(ContainSubstring("Parent received SIGTERM")) Expect(result.Stdout).To(ContainSubstring("Child received SIGTERM")) // All processes are gone expectProcessesToNotExist() }) }) Context("when parent and child do not exit after receiving SIGTERM in small amount of time", func() { It("sends kill signal to the whole group and returns with ? exit status", func() { cmd := Command{Name: filepath.Join(buildDir, "parent_ignore_term")} process, err := runner.RunComplexCommandAsync(cmd) Expect(err).ToNot(HaveOccurred()) // Wait for script to start and output pids time.Sleep(2 * time.Second) waitCh := process.Wait() err = process.TerminateNicely(2 * time.Second) Expect(err).ToNot(HaveOccurred()) result := <-waitCh Expect(result.Error).To(HaveOccurred()) // Parent exit code is returned Expect(result.ExitStatus).To(Equal(128 + 9)) // Term signal was sent to all processes in the group before kill Expect(result.Stdout).To(ContainSubstring("Parent received SIGTERM")) Expect(result.Stdout).To(ContainSubstring("Child received SIGTERM")) // Parent and child are killed expectProcessesToNotExist() }) }) Context("when parent and child already exited before calling TerminateNicely", func() { It("returns without an error since all processes are gone", func() { cmd := Command{Name: filepath.Join(buildDir, "exe_exits")} process, err := runner.RunComplexCommandAsync(cmd) Expect(err).ToNot(HaveOccurred()) // Wait for script to exit for i := 0; i < 20; i++ { if exists, _ := hasProcessesFromBuildDir(); !exists { break } if i == 19 { Fail("Expected process did not exit fast enough") } time.Sleep(500 * time.Millisecond) } waitCh := process.Wait() err = process.TerminateNicely(2 * time.Second) Expect(err).ToNot(HaveOccurred()) result := <-waitCh Expect(result.Error).ToNot(HaveOccurred()) Expect(result.Stdout).To(Equal("")) Expect(result.Stderr).To(Equal("")) Expect(result.ExitStatus).To(Equal(0)) }) }) }) }) Describe("ExecError", func() { Describe("Error", func() { It("returns error message with full stdout and full stderr to aid debugging", func() { execErr := NewExecError("fake-cmd", "fake-stdout", "fake-stderr") expectedMsg := "Running command: 'fake-cmd', stdout: 'fake-stdout', stderr: 'fake-stderr'" Expect(execErr.Error()).To(Equal(expectedMsg)) }) }) Describe("ShortError", func() { buildLines := func(start, stop int, suffix string) string { var result []string for i := start; i <= stop; i++ { result = append(result, fmt.Sprintf("%d %s", i, suffix)) } return strings.Join(result, "\n") } Context("when stdout and stderr contains more than 100 lines", func() { It("returns error message with truncated stdout and stderr to 100 lines", func() { fullStdout101 := buildLines(1, 101, "stdout") truncatedStdout100 := buildLines(2, 101, "stdout") fullStderr101 := buildLines(1, 101, "stderr") truncatedStderr100 := buildLines(2, 101, "stderr") execErr := NewExecError("fake-cmd", fullStdout101, fullStderr101) expectedMsg := fmt.Sprintf( "Running command: 'fake-cmd', stdout: '%s', stderr: '%s'", truncatedStdout100, truncatedStderr100, ) Expect(execErr.ShortError()).To(Equal(expectedMsg)) }) }) Context("when stdout and stderr contains exactly 100 lines", func() { It("returns error message with full lines", func() { stdout100 := buildLines(1, 100, "stdout") stderr100 := buildLines(1, 100, "stderr") execErr := NewExecError("fake-cmd", stdout100, stderr100) expectedMsg := fmt.Sprintf("Running command: 'fake-cmd', stdout: '%s', stderr: '%s'", stdout100, stderr100) Expect(execErr.ShortError()).To(Equal(expectedMsg)) }) }) Context("when stdout and stderr contains less than 100 lines", func() { It("returns error message with full lines", func() { stdout99 := buildLines(1, 99, "stdout") stderr99 := buildLines(1, 99, "stderr") execErr := NewExecError("fake-cmd", stdout99, stderr99) expectedMsg := fmt.Sprintf("Running command: 'fake-cmd', stdout: '%s', stderr: '%s'", stdout99, stderr99) Expect(execErr.ShortError()).To(Equal(expectedMsg)) }) }) }) }) }
func rootDesc() { var ( fakeRelease *fakebirel.FakeRelease stemcell bistemcell.ExtractedStemcell deploymentRepo *fakebiconfig.FakeDeploymentRepo releaseRepo *fakebiconfig.FakeReleaseRepo stemcellRepo *fakebiconfig.FakeStemcellRepo fakeSHA1Calculator *fakebicrypto.FakeSha1Calculator deploymentRecord Record releases []release.Release ) BeforeEach(func() { fakeRelease = &fakebirel.FakeRelease{ ReleaseName: "fake-release-name", ReleaseVersion: "fake-release-version", } releases = []release.Release{fakeRelease} fakeFS := fakesys.NewFakeFileSystem() stemcell = bistemcell.NewExtractedStemcell( bistemcell.Manifest{ Name: "fake-stemcell-name", Version: "fake-stemcell-version", }, "fake-extracted-path", fakeFS, ) deploymentRepo = fakebiconfig.NewFakeDeploymentRepo() releaseRepo = &fakebiconfig.FakeReleaseRepo{} stemcellRepo = fakebiconfig.NewFakeStemcellRepo() fakeSHA1Calculator = fakebicrypto.NewFakeSha1Calculator() deploymentRecord = NewRecord(deploymentRepo, releaseRepo, stemcellRepo, fakeSHA1Calculator) }) Describe("IsDeployed", func() { BeforeEach(func() { stemcellRecord := biconfig.StemcellRecord{ ID: "fake-stemcell-id", Name: "fake-stemcell-name", Version: "fake-stemcell-version", CID: "fake-stemcell-cid", } stemcellRepo.SetFindCurrentBehavior(stemcellRecord, true, nil) deploymentRepo.SetFindCurrentBehavior("fake-manifest-sha1", true, nil) fakeSHA1Calculator.SetCalculateBehavior(map[string]fakebicrypto.CalculateInput{ "fake-manifest-path": fakebicrypto.CalculateInput{ Sha1: "fake-manifest-sha1", Err: nil, }, }) }) Context("when the stemcell and manifest do not change", func() { Context("when no release is currently deployed", func() { BeforeEach(func() { releaseRepo.ListReturns([]biconfig.ReleaseRecord{}, nil) }) It("returns false", func() { isDeployed, err := deploymentRecord.IsDeployed("fake-manifest-path", releases, stemcell) Expect(err).ToNot(HaveOccurred()) Expect(isDeployed).To(BeFalse()) }) }) Context("when the same release is currently deployed", func() { BeforeEach(func() { releaseRecords := []biconfig.ReleaseRecord{{ ID: "fake-release-id", Name: fakeRelease.Name(), Version: fakeRelease.Version(), }} releaseRepo.ListReturns(releaseRecords, nil) }) It("returns true", func() { isDeployed, err := deploymentRecord.IsDeployed("fake-manifest-path", releases, stemcell) Expect(err).ToNot(HaveOccurred()) Expect(isDeployed).To(BeTrue()) }) }) Context("when a different version of the same release is currently deployed", func() { BeforeEach(func() { Expect("other-version").ToNot(Equal(fakeRelease.Version())) releaseRecords := []biconfig.ReleaseRecord{{ ID: "fake-release-id-2", Name: fakeRelease.Name(), Version: "other-version", }} releaseRepo.ListReturns(releaseRecords, nil) }) It("returns false", func() { isDeployed, err := deploymentRecord.IsDeployed("fake-manifest-path", releases, stemcell) Expect(err).ToNot(HaveOccurred()) Expect(isDeployed).To(BeFalse()) }) }) Context("when a same version of a different release is currently deployed", func() { BeforeEach(func() { Expect("other-release").ToNot(Equal(fakeRelease.Name())) releaseRecords := []biconfig.ReleaseRecord{{ ID: "fake-release-id-2", Name: "other-release", Version: fakeRelease.Version(), }} releaseRepo.ListReturns(releaseRecords, nil) }) It("returns false", func() { isDeployed, err := deploymentRecord.IsDeployed("fake-manifest-path", releases, stemcell) Expect(err).ToNot(HaveOccurred()) Expect(isDeployed).To(BeFalse()) }) }) Context("when deploying multiple releases", func() { var otherFakeRelease *fakebirel.FakeRelease BeforeEach(func() { otherFakeRelease = &fakebirel.FakeRelease{ ReleaseName: "other-fake-release-name", ReleaseVersion: "other-fake-release-version", } releaseRecords := []biconfig.ReleaseRecord{ { ID: "fake-release-id-1", Name: fakeRelease.Name(), Version: fakeRelease.Version(), }, { ID: "other-fake-release-id-1", Name: otherFakeRelease.Name(), Version: otherFakeRelease.Version(), }, } releaseRepo.ListReturns(releaseRecords, nil) }) Context("when the same releases are currently deployed", func() { Context("(in the same order)", func() { BeforeEach(func() { releases = []release.Release{ fakeRelease, otherFakeRelease, } }) It("returns true", func() { isDeployed, err := deploymentRecord.IsDeployed("fake-manifest-path", releases, stemcell) Expect(err).ToNot(HaveOccurred()) Expect(isDeployed).To(BeTrue()) }) }) Context("(in a different order)", func() { BeforeEach(func() { releases = []release.Release{ otherFakeRelease, fakeRelease, } }) It("returns true", func() { isDeployed, err := deploymentRecord.IsDeployed("fake-manifest-path", releases, stemcell) Expect(err).ToNot(HaveOccurred()) Expect(isDeployed).To(BeTrue()) }) }) Context("when a superset of releases is currently deployed", func() { BeforeEach(func() { releases = []release.Release{ fakeRelease, } }) It("returns false", func() { isDeployed, err := deploymentRecord.IsDeployed("fake-manifest-path", releases, stemcell) Expect(err).ToNot(HaveOccurred()) Expect(isDeployed).To(BeFalse()) }) }) }) }) }) Context("when getting current deployment manifest sha1 fails", func() { BeforeEach(func() { deploymentRepo.SetFindCurrentBehavior("fake-manifest-path", true, errors.New("fake-find-error")) }) It("returns an error", func() { _, err := deploymentRecord.IsDeployed("fake-manifest-path", releases, stemcell) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-find-error")) }) }) Context("when no deployment is set", func() { BeforeEach(func() { deploymentRepo.SetFindCurrentBehavior("", false, nil) }) It("returns false", func() { isDeployed, err := deploymentRecord.IsDeployed("fake-manifest-path", releases, stemcell) Expect(err).ToNot(HaveOccurred()) Expect(isDeployed).To(BeFalse()) }) }) Context("when calculating the deployment manifest sha1 fails", func() { BeforeEach(func() { fakeSHA1Calculator.SetCalculateBehavior(map[string]fakebicrypto.CalculateInput{ "fake-manifest-path": fakebicrypto.CalculateInput{ Sha1: "", Err: errors.New("fake-calculate-error"), }, }) }) It("returns an error", func() { _, err := deploymentRecord.IsDeployed("fake-manifest-path", releases, stemcell) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-calculate-error")) }) }) Context("when a different deployment manifest is currently deployed", func() { BeforeEach(func() { deploymentRepo.SetFindCurrentBehavior("fake-manifest-sha1-2", true, nil) }) It("returns false", func() { isDeployed, err := deploymentRecord.IsDeployed("fake-manifest-path", releases, stemcell) Expect(err).ToNot(HaveOccurred()) Expect(isDeployed).To(BeFalse()) }) }) Context("when finding the currently deployed stemcell fails", func() { BeforeEach(func() { stemcellRepo.SetFindCurrentBehavior(biconfig.StemcellRecord{}, false, errors.New("fake-find-error")) }) It("returns an error", func() { _, err := deploymentRecord.IsDeployed("fake-manifest-path", releases, stemcell) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-find-error")) }) }) Context("when no stemcell is currently deployed", func() { BeforeEach(func() { stemcellRepo.SetFindCurrentBehavior(biconfig.StemcellRecord{}, false, nil) }) It("returns false", func() { isDeployed, err := deploymentRecord.IsDeployed("fake-manifest-path", releases, stemcell) Expect(err).ToNot(HaveOccurred()) Expect(isDeployed).To(BeFalse()) }) }) Context("when a different stemcell is currently deployed", func() { BeforeEach(func() { stemcellRecord := biconfig.StemcellRecord{ ID: "fake-stemcell-id-2", Name: "fake-stemcell-name-2", Version: "fake-stemcell-version-2", CID: "fake-stemcell-cid-2", } stemcellRepo.SetFindCurrentBehavior(stemcellRecord, true, nil) }) It("returns false", func() { isDeployed, err := deploymentRecord.IsDeployed("fake-manifest-path", releases, stemcell) Expect(err).ToNot(HaveOccurred()) Expect(isDeployed).To(BeFalse()) }) }) Context("when finding the currently deployed release fails", func() { BeforeEach(func() { releaseRepo.ListReturns([]biconfig.ReleaseRecord{}, errors.New("fake-find-error")) }) It("returns an error", func() { _, err := deploymentRecord.IsDeployed("fake-manifest-path", releases, stemcell) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-find-error")) }) }) }) Describe("Update", func() { BeforeEach(func() { fakeSHA1Calculator.SetCalculateBehavior(map[string]fakebicrypto.CalculateInput{ "fake-manifest-path": fakebicrypto.CalculateInput{ Sha1: "fake-manifest-sha1", Err: nil, }, }) }) It("calculates and updates sha1 of currently deployed manifest", func() { err := deploymentRecord.Update("fake-manifest-path", releases) Expect(err).ToNot(HaveOccurred()) Expect(deploymentRepo.UpdateCurrentManifestSHA1).To(Equal("fake-manifest-sha1")) }) It("passes the releases to the release repo", func() { err := deploymentRecord.Update("fake-manifest-path", releases) Expect(err).ToNot(HaveOccurred()) Expect(releaseRepo.UpdateCallCount()).To(Equal(1)) Expect(releaseRepo.UpdateArgsForCall(0)).To(Equal(releases)) }) Context("when calculating the deployment manifest sha1 fails", func() { BeforeEach(func() { fakeSHA1Calculator.SetCalculateBehavior(map[string]fakebicrypto.CalculateInput{ "fake-manifest-path": fakebicrypto.CalculateInput{ Sha1: "", Err: errors.New("fake-calculate-error"), }, }) }) It("returns an error", func() { err := deploymentRecord.Update("fake-manifest-path", releases) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-calculate-error")) }) It("does not update the release records", func() { deploymentRecord.Update("fake-manifest-path", releases) Expect(releaseRepo.UpdateCallCount()).To(Equal(0)) }) }) Context("when updating currently deployed manifest sha1 fails", func() { BeforeEach(func() { deploymentRepo.UpdateCurrentErr = errors.New("fake-update-error") }) It("returns an error", func() { err := deploymentRecord.Update("fake-manifest-path", releases) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-update-error")) }) It("does not update the release records", func() { deploymentRecord.Update("fake-manifest-path", releases) Expect(releaseRepo.UpdateCallCount()).To(Equal(0)) }) }) Context("when updating release records fails", func() { BeforeEach(func() { releaseRepo.UpdateReturns(errors.New("fake-update-error")) }) It("returns an error", func() { err := deploymentRecord.Update("fake-manifest-path", releases) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-update-error")) }) }) }) Describe("Clear", func() { It("clears manifest hash", func() { deploymentRepo.UpdateCurrentManifestSHA1 = "initial-sha1" err := deploymentRecord.Clear() Expect(err).ToNot(HaveOccurred()) Expect(deploymentRepo.UpdateCurrentManifestSHA1).To(Equal("")) }) It("clears releases list", func() { err := deploymentRecord.Clear() Expect(err).ToNot(HaveOccurred()) Expect(releaseRepo.UpdateCallCount()).To(Equal(1)) Expect(releaseRepo.UpdateArgsForCall(0)).To(Equal([]release.Release{})) }) Context("when clearing manifest hash fails", func() { BeforeEach(func() { deploymentRepo.UpdateCurrentErr = errors.New("fake-update-error") }) It("returns an error", func() { err := deploymentRecord.Clear() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-update-error")) }) }) Context("when clearing release records fails", func() { BeforeEach(func() { releaseRepo.UpdateReturns(errors.New("fake-update-error")) }) It("returns an error", func() { err := deploymentRecord.Clear() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-update-error")) }) }) }) }
func init() { Describe("prepareNetworkChange", func() { var ( action PrepareNetworkChangeAction fs *fakesys.FakeFileSystem settingsService *fakesettings.FakeSettingsService ) BeforeEach(func() { fs = fakesys.NewFakeFileSystem() settingsService = &fakesettings.FakeSettingsService{} action = NewPrepareNetworkChange(fs, settingsService, fakeactions.NewFakeAgentKiller()) }) It("is synchronous", func() { Expect(action.IsAsynchronous()).To(BeFalse()) }) It("is not persistent", func() { Expect(action.IsPersistent()).To(BeFalse()) }) It("invalidates settings so that load settings cannot fall back on old settings", func() { resp, err := action.Run() Expect(err).NotTo(HaveOccurred()) Expect(resp).To(Equal("ok")) Expect(settingsService.SettingsWereInvalidated).To(BeTrue()) }) Context("when settings invalidation succeeds", func() { Context("when the network rules file can be removed", func() { It("removes the network rules file", func() { fs.WriteFile("/etc/udev/rules.d/70-persistent-net.rules", []byte{}) resp, err := action.Run() Expect(err).NotTo(HaveOccurred()) Expect(resp).To(Equal("ok")) Expect(fs.FileExists("/etc/udev/rules.d/70-persistent-net.rules")).To(BeFalse()) }) }) Context("when the network rules file cannot be removed", func() { BeforeEach(func() { fs.RemoveAllStub = func(_ string) error { return errors.New("fake-remove-all-error") } }) It("returns error from removing the network rules file", func() { resp, err := action.Run() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-remove-all-error")) Expect(resp).To(BeNil()) }) }) }) Context("when settings invalidation fails", func() { BeforeEach(func() { settingsService.InvalidateSettingsError = errors.New("fake-invalidate-error") }) It("returns error early if settings err invalidating", func() { resp, err := action.Run() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-invalidate-error")) Expect(resp).To(BeNil()) }) It("does not remove the network rules file", func() { fs.WriteFile("/etc/udev/rules.d/70-persistent-net.rules", []byte{}) action.Run() Expect(fs.FileExists("/etc/udev/rules.d/70-persistent-net.rules")).To(BeTrue()) }) }) }) }
func describeDummyPlatform() { var ( platform Platform collector boshstats.Collector fs *fakesys.FakeFileSystem cmdRunner boshsys.CmdRunner dirProvider boshdirs.Provider devicePathResolver boshdpresolv.DevicePathResolver logger boshlog.Logger ) BeforeEach(func() { collector = &fakestats.FakeCollector{} fs = fakesys.NewFakeFileSystem() cmdRunner = fakesys.NewFakeCmdRunner() dirProvider = boshdirs.NewProvider("/fake-dir") devicePathResolver = fakedpresolv.NewFakeDevicePathResolver() logger = boshlog.NewLogger(boshlog.LevelNone) }) JustBeforeEach(func() { platform = NewDummyPlatform( collector, fs, cmdRunner, dirProvider, devicePathResolver, logger, ) }) Describe("GetDefaultNetwork", func() { It("returns the contents of dummy-defaults-network-settings.json since that's what the dummy cpi writes", func() { settingsFilePath := "/fake-dir/bosh/dummy-default-network-settings.json" fs.WriteFileString(settingsFilePath, `{"IP": "1.2.3.4"}`) network, err := platform.GetDefaultNetwork() Expect(err).NotTo(HaveOccurred()) Expect(network.IP).To(Equal("1.2.3.4")) }) }) Describe("GetCertManager", func() { It("returns a dummy cert manager", func() { certManager := platform.GetCertManager() Expect(certManager.UpdateCertificates("")).Should(BeNil()) }) }) Describe("MountPersistentDisk", func() { var diskSettings boshsettings.DiskSettings var mountsPath, managedSettingsPath, formattedDisksPath string BeforeEach(func() { diskSettings = boshsettings.DiskSettings{ID: "somediskid"} mountsPath = filepath.Join(dirProvider.BoshDir(), "mounts.json") managedSettingsPath = filepath.Join(dirProvider.BoshDir(), "managed_disk_settings.json") formattedDisksPath = filepath.Join(dirProvider.BoshDir(), "formatted_disks.json") }) It("Mounts a persistent disk", func() { mountsContent, _ := fs.ReadFileString(mountsPath) Expect(mountsContent).To(Equal("")) err := platform.MountPersistentDisk(diskSettings, "/dev/potato") Expect(err).NotTo(HaveOccurred()) mountsContent, _ = fs.ReadFileString(mountsPath) Expect(mountsContent).To(Equal(`[{"MountDir":"/dev/potato","DiskCid":"somediskid"}]`)) }) It("Updates the managed disk settings", func() { lastMountedCid, _ := fs.ReadFileString(managedSettingsPath) Expect(lastMountedCid).To(Equal("")) err := platform.MountPersistentDisk(diskSettings, "/dev/potato") Expect(err).NotTo(HaveOccurred()) lastMountedCid, _ = fs.ReadFileString(managedSettingsPath) Expect(lastMountedCid).To(Equal("somediskid")) }) It("Updates the formatted disks", func() { formattedDisks, _ := fs.ReadFileString(formattedDisksPath) Expect(formattedDisks).To(Equal("")) err := platform.MountPersistentDisk(diskSettings, "/dev/potato") Expect(err).NotTo(HaveOccurred()) formattedDisks, _ = fs.ReadFileString(formattedDisksPath) Expect(formattedDisks).To(Equal(`[{"DiskCid":"somediskid"}]`)) }) Context("Device has already been mounted as expected", func() { BeforeEach(func() { fs.WriteFileString(managedSettingsPath, "somediskid") fs.WriteFileString(mountsPath, `[{"MountDir":"/dev/potato","DiskCid":"somediskid"}]`) }) It("Does not mount in new location", func() { err := platform.MountPersistentDisk(diskSettings, "/dev/potato") Expect(err).NotTo(HaveOccurred()) mountsContent, _ := fs.ReadFileString(mountsPath) Expect(mountsContent).To(Equal(`[{"MountDir":"/dev/potato","DiskCid":"somediskid"}]`)) }) }) }) Describe("UnmountPersistentDisk", func() { Context("when there are two mounted persistent disks in the mounts json", func() { BeforeEach(func() { var mounts []mount mounts = append(mounts, mount{MountDir: "dir1", DiskCid: "cid1"}) mounts = append(mounts, mount{MountDir: "dir2", DiskCid: "cid2"}) mountsJSON, err := json.Marshal(mounts) Expect(err).NotTo(HaveOccurred()) mountsPath := filepath.Join(dirProvider.BoshDir(), "mounts.json") fs.WriteFile(mountsPath, mountsJSON) }) It("removes one of the disks from the mounts json", func() { unmounted, err := platform.UnmountPersistentDisk(settings.DiskSettings{ID: "cid1"}) Expect(err).NotTo(HaveOccurred()) Expect(unmounted).To(Equal(true)) _, isMountPoint, err := platform.IsMountPoint("dir1") Expect(err).NotTo(HaveOccurred()) Expect(isMountPoint).To(Equal(false)) _, isMountPoint, err = platform.IsMountPoint("dir2") Expect(err).NotTo(HaveOccurred()) Expect(isMountPoint).To(Equal(true)) }) }) }) Describe("SetDiskAssociations", func() { It("writes the associations to the file", func() { diskName1 := "disk1" diskName2 := "disk2" err := platform.AssociateDisk(diskName1, boshsettings.DiskSettings{}) Expect(err).NotTo(HaveOccurred()) err = platform.AssociateDisk(diskName2, boshsettings.DiskSettings{}) Expect(err).NotTo(HaveOccurred()) diskAssociationsPath := filepath.Join(dirProvider.BoshDir(), "disk_associations.json") actualDiskNames := []string{} fileContent, err := fs.ReadFile(diskAssociationsPath) Expect(err).NotTo(HaveOccurred()) err = json.Unmarshal(fileContent, &actualDiskNames) Expect(err).NotTo(HaveOccurred()) Expect(actualDiskNames).To(ConsistOf([]string{ diskName1, diskName2, })) }) }) Describe("IsPersistentDiskMountable", func() { BeforeEach(func() { formattedDisksPath := filepath.Join(dirProvider.BoshDir(), "formatted_disks.json") fs.WriteFileString(formattedDisksPath, `[{"DiskCid": "my-disk-id"}]`) }) Context("when disk has been formatted", func() { It("returns true with no error", func() { diskSettings := boshsettings.DiskSettings{ID: "my-disk-id"} mountable, err := platform.IsPersistentDiskMountable(diskSettings) Expect(err).ToNot(HaveOccurred()) Expect(mountable).To(Equal(true)) }) }) Context("when disk has NOT been formatted", func() { It("returns false with no error", func() { diskSettings := boshsettings.DiskSettings{ID: "some-other-disk-id"} mountable, err := platform.IsPersistentDiskMountable(diskSettings) Expect(err).ToNot(HaveOccurred()) Expect(mountable).To(Equal(false)) }) }) }) Describe("SetUserPassword", func() { It("writes the password to a file", func() { err := platform.SetUserPassword("user-name", "fake-password") Expect(err).NotTo(HaveOccurred()) userPasswordsPath := filepath.Join(dirProvider.BoshDir(), "user-name", CredentialFileName) password, err := fs.ReadFileString(userPasswordsPath) Expect(err).NotTo(HaveOccurred()) Expect(password).To(Equal("fake-password")) }) It("writes the passwords to different files for each user", func() { err := platform.SetUserPassword("user-name1", "fake-password1") Expect(err).NotTo(HaveOccurred()) err = platform.SetUserPassword("user-name2", "fake-password2") Expect(err).NotTo(HaveOccurred()) userPasswordsPath := filepath.Join(dirProvider.BoshDir(), "user-name1", CredentialFileName) password, err := fs.ReadFileString(userPasswordsPath) Expect(err).NotTo(HaveOccurred()) Expect(password).To(Equal("fake-password1")) userPasswordsPath = filepath.Join(dirProvider.BoshDir(), "user-name2", CredentialFileName) password, err = fs.ReadFileString(userPasswordsPath) Expect(err).NotTo(HaveOccurred()) Expect(password).To(Equal("fake-password2")) }) }) Describe("SetupDataDir", func() { It("creates a link from BASEDIR/sys to BASEDIR/data/sys", func() { err := platform.SetupDataDir() Expect(err).NotTo(HaveOccurred()) stat := fs.GetFileTestStat(filepath.Clean("/fake-dir/sys")) Expect(stat).ToNot(BeNil()) Expect(stat.SymlinkTarget).To(Equal("/fake-dir/data/sys")) }) }) Describe("SetupBlobsDir", func() { It("creates a blobs folder under BASEDIR/DATADIR with correct permissions", func() { err := platform.SetupBlobsDir() Expect(err).NotTo(HaveOccurred()) stat := fs.GetFileTestStat(filepath.Clean("/fake-dir/data/blobs")) Expect(stat.FileType).To(Equal(fakesys.FakeFileTypeDir)) Expect(stat.FileMode).To(Equal(os.FileMode(0700))) }) }) }
package ntp_test import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" . "github.com/cloudfoundry/bosh-agent/platform/ntp" boshdir "github.com/cloudfoundry/bosh-agent/settings/directories" fakefs "github.com/cloudfoundry/bosh-utils/system/fakes" ) var _ = Describe("concreteService", func() { Describe("GetInfo", func() { buildService := func(NTPData string) Service { fs := fakefs.NewFakeFileSystem() dirProvider := boshdir.NewProvider("/var/vcap") if NTPData != "" { err := fs.WriteFileString("/var/vcap/bosh/log/ntpdate.out", NTPData) Expect(err).ToNot(HaveOccurred()) } return NewConcreteService(fs, dirProvider) } It("returns valid offset", func() { NTPData := `server 10.16.45.209, stratum 2, offset -0.081236, delay 0.04291 12 Oct 17:37:58 ntpdate[42757]: adjust time server 10.16.45.209 offset -0.081236 sec ` service := buildService(NTPData)
func describeUbuntuNetManager() { var ( fs *fakesys.FakeFileSystem cmdRunner *fakesys.FakeCmdRunner ipResolver *fakeip.FakeResolver addressBroadcaster *fakearp.FakeAddressBroadcaster interfaceAddrsProvider *fakeip.FakeInterfaceAddressesProvider 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{} interfaceAddrsProvider = &fakeip.FakeInterfaceAddressesProvider{} interfaceAddrsValidator := boship.NewInterfaceAddressesValidator(interfaceAddrsProvider) dnsValidator := NewDNSValidator(fs) netManager = NewUbuntuNetManager( fs, cmdRunner, ipResolver, interfaceConfigurationCreator, interfaceAddrsValidator, dnsValidator, 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", IsDefaultForGateway: true, 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", Default: []string{"gateway"}, Netmask: "255.255.255.0", Gateway: "3.4.5.6", Mac: "fake-static-mac-address", } interfaceAddrsProvider.GetInterfaceAddresses = []boship.InterfaceAddress{ boship.NewSimpleInterfaceAddress("ethstatic", "1.2.3.4"), } fs.WriteFileString("/etc/resolv.conf", ` nameserver 8.8.8.8 nameserver 9.9.9.9 `) 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("configures gateway, broadcast and dns for default network only", func() { 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", } secondStaticNetwork := boshsettings.Network{ Type: "manual", IP: "5.6.7.8", Netmask: "255.255.255.0", Gateway: "6.7.8.9", Mac: "second-fake-static-mac-address", DNS: []string{"8.8.8.8"}, Default: []string{"gateway", "dns"}, } stubInterfaces(map[string]boshsettings.Network{ "eth0": staticNetwork, "eth1": secondStaticNetwork, }) interfaceAddrsProvider.GetInterfaceAddresses = []boship.InterfaceAddress{ boship.NewSimpleInterfaceAddress("eth0", "1.2.3.4"), boship.NewSimpleInterfaceAddress("eth1", "5.6.7.8"), } err := netManager.SetupNetworking(boshsettings.Networks{ "static-1": staticNetwork, "static-2": secondStaticNetwork, }, 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 eth0 iface eth0 inet static address 1.2.3.4 network 1.2.3.0 netmask 255.255.255.0 auto eth1 iface eth1 inet static address 5.6.7.8 network 5.6.7.0 netmask 255.255.255.0 broadcast 5.6.7.255 gateway 6.7.8.9 dns-nameservers 8.8.8.8`)) }) 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", Default: []string{"gateway"}, 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", "--force", "ethdhcp", "ethstatic"})) Expect(cmdRunner.RunCommands[4]).To(Equal([]string{"ifup", "--force", "ethdhcp", "ethstatic"})) }) 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", "--force", "ethdhcp", "ethstatic"})) Expect(cmdRunner.RunCommands[4]).To(Equal([]string{"ifup", "--force", "ethdhcp", "ethstatic"})) }) 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 manual networks were not configured with proper IP addresses", func() { BeforeEach(func() { interfaceAddrsProvider.GetInterfaceAddresses = []boship.InterfaceAddress{ boship.NewSimpleInterfaceAddress("ethstatic", "1.2.3.5"), } }) It("fails", func() { stubInterfaces(map[string]boshsettings.Network{ "ethstatic": staticNetwork, }) errCh := make(chan error) err := netManager.SetupNetworking(boshsettings.Networks{"static-network": staticNetwork}, errCh) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Validating static network configuration")) }) }) Context("when dns is not properly configured", func() { BeforeEach(func() { fs.WriteFileString("/etc/resolv.conf", "") }) It("fails", func() { staticNetwork = boshsettings.Network{ Type: "manual", IP: "1.2.3.4", Default: []string{"dns"}, DNS: []string{"8.8.8.8"}, Netmask: "255.255.255.0", Gateway: "3.4.5.6", Mac: "fake-static-mac-address", } stubInterfaces(map[string]boshsettings.Network{ "ethstatic": staticNetwork, }) errCh := make(chan error) err := netManager.SetupNetworking(boshsettings.Networks{"static-network": staticNetwork}, errCh) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Validating dns configuration")) }) }) 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", Default: []string{"gateway"}, Netmask: "255.255.255.0", Gateway: "3.4.5.6", } stubInterfaces( map[string]boshsettings.Network{ "ethstatic": staticNetwork, }, ) interfaceAddrsProvider.GetInterfaceAddresses = []boship.InterfaceAddress{ boship.NewSimpleInterfaceAddress("ethstatic", "2.2.2.2"), } 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", Default: []string{"gateway"}, Netmask: "255.255.255.0", Gateway: "3.4.5.6", } stubInterfacesWithVirtual( map[string]boshsettings.Network{ "ethstatic": staticNetwork, }, []string{"virtual"}, ) interfaceAddrsProvider.GetInterfaceAddresses = []boship.InterfaceAddress{ boship.NewSimpleInterfaceAddress("ethstatic", "2.2.2.2"), } 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("Testing with Ginkgo", func() { It("linux format when using swap fs", func() { fakeRunner := fakesys.NewFakeCmdRunner() fakeFs := fakesys.NewFakeFileSystem() fakeRunner.AddCmdResult("blkid -p /dev/xvda1", fakesys.FakeCmdResult{Stdout: `xxxxx TYPE="ext4" yyyy zzzz`}) formatter := NewLinuxFormatter(fakeRunner, fakeFs) formatter.Format("/dev/xvda1", FileSystemSwap) Expect(2).To(Equal(len(fakeRunner.RunCommands))) Expect(fakeRunner.RunCommands[1]).To(Equal([]string{"mkswap", "/dev/xvda1"})) }) It("linux format when using swap fs and partition is swap", func() { fakeRunner := fakesys.NewFakeCmdRunner() fakeFs := fakesys.NewFakeFileSystem() fakeRunner.AddCmdResult("blkid -p /dev/xvda1", fakesys.FakeCmdResult{Stdout: `xxxxx TYPE="swap" yyyy zzzz`}) formatter := NewLinuxFormatter(fakeRunner, fakeFs) formatter.Format("/dev/xvda1", FileSystemSwap) Expect(1).To(Equal(len(fakeRunner.RunCommands))) Expect(fakeRunner.RunCommands[0]).To(Equal([]string{"blkid", "-p", "/dev/xvda1"})) }) It("linux format when using ext4 fs with lazy itable support", func() { fakeRunner := fakesys.NewFakeCmdRunner() fakeFs := fakesys.NewFakeFileSystem() fakeFs.WriteFile("/sys/fs/ext4/features/lazy_itable_init", []byte{}) fakeRunner.AddCmdResult("blkid -p /dev/xvda1", fakesys.FakeCmdResult{Stdout: `xxxxx TYPE="ext2" yyyy zzzz`}) formatter := NewLinuxFormatter(fakeRunner, fakeFs) formatter.Format("/dev/xvda2", FileSystemExt4) Expect(2).To(Equal(len(fakeRunner.RunCommands))) Expect(fakeRunner.RunCommands[1]).To(Equal([]string{"mke2fs", "-t", "ext4", "-j", "-E", "lazy_itable_init=1", "/dev/xvda2"})) }) It("linux format when using ext4 fs without lazy itable support", func() { fakeRunner := fakesys.NewFakeCmdRunner() fakeFs := fakesys.NewFakeFileSystem() fakeRunner.AddCmdResult("blkid -p /dev/xvda1", fakesys.FakeCmdResult{Stdout: `xxxxx TYPE="ext2" yyyy zzzz`}) formatter := NewLinuxFormatter(fakeRunner, fakeFs) formatter.Format("/dev/xvda2", FileSystemExt4) Expect(2).To(Equal(len(fakeRunner.RunCommands))) Expect(fakeRunner.RunCommands[1]).To(Equal([]string{"mke2fs", "-t", "ext4", "-j", "/dev/xvda2"})) }) It("linux format when using ext4 fs and partition is ext4", func() { fakeRunner := fakesys.NewFakeCmdRunner() fakeFs := fakesys.NewFakeFileSystem() fakeRunner.AddCmdResult("blkid -p /dev/xvda1", fakesys.FakeCmdResult{Stdout: `xxxxx TYPE="ext4" yyyy zzzz`}) formatter := NewLinuxFormatter(fakeRunner, fakeFs) formatter.Format("/dev/xvda1", FileSystemExt4) Expect(1).To(Equal(len(fakeRunner.RunCommands))) Expect(fakeRunner.RunCommands[0]).To(Equal([]string{"blkid", "-p", "/dev/xvda1"})) }) }) }
func createBlobManager() (blobManager BlobManager, fs *fakesys.FakeFileSystem) { fs = fakesys.NewFakeFileSystem() blobManager = NewBlobManager(fs, "/var/vcap/micro_bosh/data/cache") return }
func rootDesc() { var mockCtrl *gomock.Controller BeforeEach(func() { mockCtrl = gomock.NewController(GinkgoT()) }) AfterEach(func() { mockCtrl.Finish() }) Describe("Run", func() { var ( command bicmd.Cmd fakeFs *fakesys.FakeFileSystem stdOut *gbytes.Buffer stdErr *gbytes.Buffer userInterface biui.UI sha1Calculator crypto.SHA1Calculator manifestSHA1 string mockDeployer *mock_deployment.MockDeployer mockInstaller *mock_install.MockInstaller mockInstallerFactory *mock_install.MockInstallerFactory mockReleaseExtractor *mock_release.MockExtractor releaseManager birel.Manager mockRegistryServerManager *mock_registry.MockServerManager mockRegistryServer *mock_registry.MockServer mockAgentClient *mock_agentclient.MockAgentClient mockAgentClientFactory *mock_httpagent.MockAgentClientFactory mockCloudFactory *mock_cloud.MockFactory fakeCPIRelease *fakebirel.FakeRelease logger boshlog.Logger mockBlobstoreFactory *mock_blobstore.MockFactory mockBlobstore *mock_blobstore.MockBlobstore mockVMManagerFactory *mock_vm.MockManagerFactory fakeVMManager *fakebivm.FakeManager fakeStemcellExtractor *fakebistemcell.FakeExtractor mockStemcellManager *mock_stemcell.MockManager fakeStemcellManagerFactory *fakebistemcell.FakeManagerFactory fakeReleaseSetParser *fakebirelsetmanifest.FakeParser fakeInstallationParser *fakebiinstallmanifest.FakeParser fakeDeploymentParser *fakebideplmanifest.FakeParser mockLegacyDeploymentStateMigrator *mock_config.MockLegacyDeploymentStateMigrator setupDeploymentStateService biconfig.DeploymentStateService fakeDeploymentValidator *fakebideplval.FakeValidator directorID = "generated-director-uuid" fakeUUIDGenerator *fakeuuid.FakeGenerator configUUIDGenerator *fakeuuid.FakeGenerator fakeStage *fakebiui.FakeStage deploymentManifestPath string deploymentStatePath string cpiReleaseTarballPath string stemcellTarballPath string extractedStemcell bistemcell.ExtractedStemcell expectDeploy *gomock.Call mbusURL = "http://*****:*****@fake-mbus-endpoint" releaseSetManifest birelsetmanifest.Manifest boshDeploymentManifest bideplmanifest.Manifest installationManifest biinstallmanifest.Manifest cloud bicloud.Cloud cloudStemcell bistemcell.CloudStemcell expectLegacyMigrate *gomock.Call expectStemcellUpload *gomock.Call expectStemcellDeleteUnused *gomock.Call expectCPIReleaseExtract *gomock.Call expectInstall *gomock.Call expectNewCloud *gomock.Call ) BeforeEach(func() { logger = boshlog.NewLogger(boshlog.LevelNone) stdOut = gbytes.NewBuffer() stdErr = gbytes.NewBuffer() userInterface = biui.NewWriterUI(stdOut, stdErr, logger) fakeFs = fakesys.NewFakeFileSystem() fakeFs.EnableStrictTempRootBehavior() deploymentManifestPath = "/path/to/manifest.yml" deploymentStatePath = "/path/to/manifest-state.json" fakeFs.RegisterOpenFile(deploymentManifestPath, &fakesys.FakeFile{ Stats: &fakesys.FakeFileStats{FileType: fakesys.FakeFileTypeFile}, }) fakeFs.WriteFileString(deploymentManifestPath, "") mockDeployer = mock_deployment.NewMockDeployer(mockCtrl) mockInstaller = mock_install.NewMockInstaller(mockCtrl) mockInstallerFactory = mock_install.NewMockInstallerFactory(mockCtrl) mockReleaseExtractor = mock_release.NewMockExtractor(mockCtrl) releaseManager = birel.NewManager(logger) mockRegistryServerManager = mock_registry.NewMockServerManager(mockCtrl) mockRegistryServer = mock_registry.NewMockServer(mockCtrl) mockAgentClientFactory = mock_httpagent.NewMockAgentClientFactory(mockCtrl) mockAgentClient = mock_agentclient.NewMockAgentClient(mockCtrl) mockAgentClientFactory.EXPECT().NewAgentClient(gomock.Any(), gomock.Any()).Return(mockAgentClient).AnyTimes() mockCloudFactory = mock_cloud.NewMockFactory(mockCtrl) mockBlobstoreFactory = mock_blobstore.NewMockFactory(mockCtrl) mockBlobstore = mock_blobstore.NewMockBlobstore(mockCtrl) mockBlobstoreFactory.EXPECT().Create(mbusURL, gomock.Any()).Return(mockBlobstore, nil).AnyTimes() mockVMManagerFactory = mock_vm.NewMockManagerFactory(mockCtrl) fakeVMManager = fakebivm.NewFakeManager() mockVMManagerFactory.EXPECT().NewManager(gomock.Any(), mockAgentClient).Return(fakeVMManager).AnyTimes() fakeStemcellExtractor = fakebistemcell.NewFakeExtractor() mockStemcellManager = mock_stemcell.NewMockManager(mockCtrl) fakeStemcellManagerFactory = fakebistemcell.NewFakeManagerFactory() fakeReleaseSetParser = fakebirelsetmanifest.NewFakeParser() fakeInstallationParser = fakebiinstallmanifest.NewFakeParser() fakeDeploymentParser = fakebideplmanifest.NewFakeParser() mockLegacyDeploymentStateMigrator = mock_config.NewMockLegacyDeploymentStateMigrator(mockCtrl) configUUIDGenerator = &fakeuuid.FakeGenerator{} configUUIDGenerator.GeneratedUUID = directorID setupDeploymentStateService = biconfig.NewFileSystemDeploymentStateService(fakeFs, configUUIDGenerator, logger, biconfig.DeploymentStatePath(deploymentManifestPath)) fakeDeploymentValidator = fakebideplval.NewFakeValidator() fakeStage = fakebiui.NewFakeStage() sha1Calculator = crypto.NewSha1Calculator(fakeFs) fakeUUIDGenerator = &fakeuuid.FakeGenerator{} var err error manifestSHA1, err = sha1Calculator.Calculate(deploymentManifestPath) Expect(err).ToNot(HaveOccurred()) cpiReleaseTarballPath = "/release/tarball/path" stemcellTarballPath = "/stemcell/tarball/path" extractedStemcell = bistemcell.NewExtractedStemcell( bistemcell.Manifest{ ImagePath: "/stemcell/image/path", Name: "fake-stemcell-name", Version: "fake-stemcell-version", SHA1: "fake-stemcell-sha1", CloudProperties: biproperty.Map{}, OS: "ubuntu-trusty", }, "fake-extracted-path", fakeFs, ) // create input files fakeFs.WriteFileString(cpiReleaseTarballPath, "") fakeFs.WriteFileString(stemcellTarballPath, "") // deployment exists fakeFs.WriteFileString(deploymentManifestPath, "") // deployment is valid fakeDeploymentValidator.SetValidateBehavior([]fakebideplval.ValidateOutput{ {Err: nil}, }) fakeDeploymentValidator.SetValidateReleaseJobsBehavior([]fakebideplval.ValidateReleaseJobsOutput{ {Err: nil}, }) // stemcell exists fakeFs.WriteFile(stemcellTarballPath, []byte{}) releaseSetManifest = birelsetmanifest.Manifest{ Releases: []birelmanifest.ReleaseRef{ { Name: "fake-cpi-release-name", URL: "file://" + cpiReleaseTarballPath, }, }, } // parsed CPI deployment manifest installationManifest = biinstallmanifest.Manifest{ Template: biinstallmanifest.ReleaseJobRef{ Name: "fake-cpi-release-job-name", Release: "fake-cpi-release-name", }, Mbus: mbusURL, } // parsed BOSH deployment manifest boshDeploymentManifest = bideplmanifest.Manifest{ Name: "fake-deployment-name", Jobs: []bideplmanifest.Job{ { Name: "fake-job-name", }, }, ResourcePools: []bideplmanifest.ResourcePool{ { Stemcell: bideplmanifest.StemcellRef{ URL: "file://" + stemcellTarballPath, }, }, }, } fakeDeploymentParser.ParseManifest = boshDeploymentManifest // parsed/extracted CPI release fakeCPIRelease = fakebirel.NewFakeRelease() fakeCPIRelease.ReleaseName = "fake-cpi-release-name" fakeCPIRelease.ReleaseVersion = "1.0" fakeCPIRelease.ReleaseIsCompiled = false fakeCPIRelease.ReleaseJobs = []bireljob.Job{ { Name: "fake-cpi-release-job-name", Templates: map[string]string{ "templates/cpi.erb": "bin/cpi", }, }, } cloud = bicloud.NewCloud(fakebicloud.NewFakeCPICmdRunner(), "fake-director-id", logger) cloudStemcell = fakebistemcell.NewFakeCloudStemcell("fake-stemcell-cid", "fake-stemcell-name", "fake-stemcell-version") }) JustBeforeEach(func() { doGet := func(deploymentManifestPath string) (bicmd.DeploymentPreparer, error) { deploymentStateService := biconfig.NewFileSystemDeploymentStateService(fakeFs, configUUIDGenerator, logger, biconfig.DeploymentStatePath(deploymentManifestPath)) deploymentRepo := biconfig.NewDeploymentRepo(deploymentStateService) releaseRepo := biconfig.NewReleaseRepo(deploymentStateService, fakeUUIDGenerator) stemcellRepo := biconfig.NewStemcellRepo(deploymentStateService, fakeUUIDGenerator) deploymentRecord := deployment.NewRecord(deploymentRepo, releaseRepo, stemcellRepo, sha1Calculator) fakeHTTPClient := fakebihttpclient.NewFakeHTTPClient() tarballCache := bitarball.NewCache("fake-base-path", fakeFs, logger) tarballProvider := bitarball.NewProvider(tarballCache, fakeFs, fakeHTTPClient, sha1Calculator, 1, 0, logger) cpiInstaller := bicpirel.CpiInstaller{ ReleaseManager: releaseManager, InstallerFactory: mockInstallerFactory, Validator: bicpirel.NewValidator(), } releaseFetcher := birel.NewFetcher(tarballProvider, mockReleaseExtractor, releaseManager) stemcellFetcher := bistemcell.Fetcher{ TarballProvider: tarballProvider, StemcellExtractor: fakeStemcellExtractor, } releaseSetAndInstallationManifestParser := bicmd.ReleaseSetAndInstallationManifestParser{ ReleaseSetParser: fakeReleaseSetParser, InstallationParser: fakeInstallationParser, } deploymentManifestParser := bicmd.DeploymentManifestParser{ DeploymentParser: fakeDeploymentParser, DeploymentValidator: fakeDeploymentValidator, ReleaseManager: releaseManager, } fakeInstallationUUIDGenerator := &fakeuuid.FakeGenerator{} fakeInstallationUUIDGenerator.GeneratedUUID = "fake-installation-id" targetProvider := biinstall.NewTargetProvider( deploymentStateService, fakeInstallationUUIDGenerator, filepath.Join("fake-install-dir"), ) tempRootConfigurator := bicmd.NewTempRootConfigurator(fakeFs) return bicmd.NewDeploymentPreparer( userInterface, logger, "deployCmd", deploymentStateService, mockLegacyDeploymentStateMigrator, releaseManager, deploymentRecord, mockCloudFactory, fakeStemcellManagerFactory, mockAgentClientFactory, mockVMManagerFactory, mockBlobstoreFactory, mockDeployer, deploymentManifestPath, cpiInstaller, releaseFetcher, stemcellFetcher, releaseSetAndInstallationManifestParser, deploymentManifestParser, tempRootConfigurator, targetProvider, ), nil } command = bicmd.NewDeployCmd(userInterface, fakeFs, logger, doGet) expectLegacyMigrate = mockLegacyDeploymentStateMigrator.EXPECT().MigrateIfExists("/path/to/bosh-deployments.yml").AnyTimes() fakeStemcellExtractor.SetExtractBehavior(stemcellTarballPath, extractedStemcell, nil) fakeStemcellManagerFactory.SetNewManagerBehavior(cloud, mockStemcellManager) expectStemcellUpload = mockStemcellManager.EXPECT().Upload(extractedStemcell, fakeStage).Return(cloudStemcell, nil).AnyTimes() expectStemcellDeleteUnused = mockStemcellManager.EXPECT().DeleteUnused(fakeStage).AnyTimes() fakeReleaseSetParser.ParseManifest = releaseSetManifest fakeDeploymentParser.ParseManifest = boshDeploymentManifest fakeInstallationParser.ParseManifest = installationManifest installationPath := filepath.Join("fake-install-dir", "fake-installation-id") target := biinstall.NewTarget(installationPath) installedJob := biinstall.NewInstalledJob( biinstall.RenderedJobRef{ Name: "fake-cpi-release-job-name", }, filepath.Join(target.JobsPath(), "fake-cpi-release-job-name"), ) mockInstallerFactory.EXPECT().NewInstaller(target).Return(mockInstaller).AnyTimes() installation := biinstall.NewInstallation(target, installedJob, installationManifest, mockRegistryServerManager) expectInstall = mockInstaller.EXPECT().Install(installationManifest, gomock.Any()).Do(func(_ interface{}, stage biui.Stage) { Expect(fakeStage.SubStages).To(ContainElement(stage)) }).Return(installation, nil).AnyTimes() mockInstaller.EXPECT().Cleanup(installation).AnyTimes() mockDeployment := mock_deployment.NewMockDeployment(mockCtrl) expectDeploy = mockDeployer.EXPECT().Deploy( cloud, boshDeploymentManifest, cloudStemcell, installationManifest.Registry, fakeVMManager, mockBlobstore, gomock.Any(), ).Do(func(_, _, _, _, _, _ interface{}, stage biui.Stage) { Expect(fakeStage.SubStages).To(ContainElement(stage)) }).Return(mockDeployment, nil).AnyTimes() expectCPIReleaseExtract = mockReleaseExtractor.EXPECT().Extract(cpiReleaseTarballPath).Return(fakeCPIRelease, nil).AnyTimes() expectNewCloud = mockCloudFactory.EXPECT().NewCloud(installation, directorID).Return(cloud, nil).AnyTimes() }) It("prints the deployment manifest and state file", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(stdOut).To(gbytes.Say("Deployment manifest: '/path/to/manifest.yml'")) Expect(stdOut).To(gbytes.Say("Deployment state: '/path/to/manifest-state.json'")) }) It("does not migrate the legacy bosh-deployments.yml if manifest-state.json exists", func() { err := fakeFs.WriteFileString(deploymentStatePath, "{}") Expect(err).ToNot(HaveOccurred()) expectLegacyMigrate.Times(0) err = command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(fakeInstallationParser.ParsePath).To(Equal(deploymentManifestPath)) }) It("migrates the legacy bosh-deployments.yml if manifest-state.json does not exist", func() { err := fakeFs.RemoveAll(deploymentStatePath) Expect(err).ToNot(HaveOccurred()) expectLegacyMigrate.Return(true, nil).Times(1) err = command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(fakeInstallationParser.ParsePath).To(Equal(deploymentManifestPath)) Expect(stdOut).To(gbytes.Say("Deployment manifest: '/path/to/manifest.yml'")) Expect(stdOut).To(gbytes.Say("Deployment state: '/path/to/manifest-state.json'")) Expect(stdOut).To(gbytes.Say("Migrated legacy deployments file: '/path/to/bosh-deployments.yml'")) }) It("sets the temp root", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(fakeFs.TempRootPath).To(Equal("fake-install-dir/fake-installation-id/tmp")) }) Context("when setting the temp root fails", func() { It("returns an error", func() { fakeFs.ChangeTempRootErr = errors.New("fake ChangeTempRootErr") err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(Equal("Setting temp root: fake ChangeTempRootErr")) }) }) It("parses the installation manifest", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(fakeInstallationParser.ParsePath).To(Equal(deploymentManifestPath)) }) It("parses the deployment manifest", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(fakeDeploymentParser.ParsePath).To(Equal(deploymentManifestPath)) }) It("validates bosh deployment manifest", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(fakeDeploymentValidator.ValidateInputs).To(Equal([]fakebideplval.ValidateInput{ {Manifest: boshDeploymentManifest, ReleaseSetManifest: releaseSetManifest}, })) }) It("validates jobs in manifest refer to job in releases", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(fakeDeploymentValidator.ValidateReleaseJobsInputs).To(Equal([]fakebideplval.ValidateReleaseJobsInput{ {Manifest: boshDeploymentManifest, ReleaseManager: releaseManager}, })) }) It("logs validating stages", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(fakeStage.PerformCalls[0]).To(Equal(&fakebiui.PerformCall{ Name: "validating", Stage: &fakebiui.FakeStage{ PerformCalls: []*fakebiui.PerformCall{ {Name: "Validating release 'fake-cpi-release-name'"}, {Name: "Validating cpi release"}, {Name: "Validating deployment manifest"}, {Name: "Validating stemcell"}, }, }, })) }) It("extracts CPI release tarball", func() { expectCPIReleaseExtract.Times(1) err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) }) It("installs the CPI locally", func() { expectInstall.Times(1) expectNewCloud.Times(1) err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) }) It("adds a new 'installing CPI' event logger stage", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(fakeStage.PerformCalls[1]).To(Equal(&fakebiui.PerformCall{ Name: "installing CPI", Stage: &fakebiui.FakeStage{}, // mock installer doesn't add sub-stages })) }) It("adds a new 'Starting registry' event logger stage", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(fakeStage.PerformCalls[2]).To(Equal(&fakebiui.PerformCall{ Name: "Starting registry", })) }) Context("when the registry is configured", func() { BeforeEach(func() { installationManifest.Registry = biinstallmanifest.Registry{ Username: "******", Password: "******", Host: "fake-host", Port: 123, } }) It("starts & stops the registry", func() { mockRegistryServerManager.EXPECT().Start("fake-username", "fake-password", "fake-host", 123).Return(mockRegistryServer, nil) mockRegistryServer.EXPECT().Stop() err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) }) }) It("deletes the extracted CPI release", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(fakeCPIRelease.DeleteCalled).To(BeTrue()) }) It("extracts the stemcell", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(fakeStemcellExtractor.ExtractInputs).To(Equal([]fakebistemcell.ExtractInput{ {TarballPath: stemcellTarballPath}, })) }) It("uploads the stemcell", func() { expectStemcellUpload.Times(1) err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).ToNot(HaveOccurred()) }) It("adds a new 'deploying' event logger stage", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(fakeStage.PerformCalls[3]).To(Equal(&fakebiui.PerformCall{ Name: "deploying", Stage: &fakebiui.FakeStage{}, // mock deployer doesn't add sub-stages })) }) It("deploys", func() { expectDeploy.Times(1) err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) }) It("updates the deployment record", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) deploymentState, err := setupDeploymentStateService.Load() Expect(err).ToNot(HaveOccurred()) Expect(deploymentState.CurrentManifestSHA1).To(Equal(manifestSHA1)) Expect(deploymentState.Releases).To(Equal([]biconfig.ReleaseRecord{ { ID: "fake-uuid-0", Name: fakeCPIRelease.Name(), Version: fakeCPIRelease.Version(), }, })) }) It("deletes unused stemcells", func() { expectStemcellDeleteUnused.Times(1) err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) }) Context("when deployment has not changed", func() { JustBeforeEach(func() { previousDeploymentState := biconfig.DeploymentState{ DirectorID: directorID, CurrentReleaseIDs: []string{"my-release-id-1"}, Releases: []biconfig.ReleaseRecord{{ ID: "my-release-id-1", Name: fakeCPIRelease.Name(), Version: fakeCPIRelease.Version(), }}, CurrentStemcellID: "my-stemcellRecordID", Stemcells: []biconfig.StemcellRecord{{ ID: "my-stemcellRecordID", Name: cloudStemcell.Name(), Version: cloudStemcell.Version(), }}, CurrentManifestSHA1: manifestSHA1, } err := setupDeploymentStateService.Save(previousDeploymentState) Expect(err).ToNot(HaveOccurred()) }) It("skips deploy", func() { expectDeploy.Times(0) err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(stdOut).To(gbytes.Say("No deployment, stemcell or release changes. Skipping deploy.")) }) }) Context("when parsing the cpi deployment manifest fails", func() { BeforeEach(func() { fakeDeploymentParser.ParseErr = bosherr.Error("fake-parse-error") }) It("returns error", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Parsing deployment manifest")) Expect(err.Error()).To(ContainSubstring("fake-parse-error")) Expect(fakeDeploymentParser.ParsePath).To(Equal(deploymentManifestPath)) }) }) Context("when the cpi release does not contain a 'cpi' job", func() { BeforeEach(func() { fakeCPIRelease.ReleaseJobs = []bireljob.Job{ { Name: "not-cpi", }, } }) It("returns error", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(Equal("Invalid CPI release 'fake-cpi-release-name': CPI release must contain specified job 'fake-cpi-release-job-name'")) }) }) Context("when multiple releases are given", func() { var ( otherReleaseTarballPath string fakeOtherRelease *fakebirel.FakeRelease expectOtherReleaseExtract *gomock.Call ) BeforeEach(func() { otherReleaseTarballPath = "/path/to/other-release.tgz" fakeFs.WriteFileString(otherReleaseTarballPath, "") fakeOtherRelease = fakebirel.New("other-release", "1234") fakeOtherRelease.ReleaseJobs = []bireljob.Job{{Name: "not-cpi"}} expectOtherReleaseExtract = mockReleaseExtractor.EXPECT().Extract( otherReleaseTarballPath, ).Return(fakeOtherRelease, nil).AnyTimes() releaseSetManifest = birelsetmanifest.Manifest{ Releases: []birelmanifest.ReleaseRef{ { Name: "fake-cpi-release-name", URL: "file://" + cpiReleaseTarballPath, }, { Name: "other-release", URL: "file://" + otherReleaseTarballPath, }, }, } }) It("extracts all the release tarballs", func() { expectCPIReleaseExtract.Times(1) expectOtherReleaseExtract.Times(1) err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) }) It("installs the CPI release locally", func() { expectInstall.Times(1) expectNewCloud.Times(1) err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) }) It("updates the deployment record", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) deploymentState, err := setupDeploymentStateService.Load() Expect(err).ToNot(HaveOccurred()) Expect(deploymentState.CurrentManifestSHA1).To(Equal(manifestSHA1)) Expect(deploymentState.Releases).To(Equal([]biconfig.ReleaseRecord{ { ID: "fake-uuid-0", Name: fakeCPIRelease.Name(), Version: fakeCPIRelease.Version(), }, { ID: "fake-uuid-1", Name: fakeOtherRelease.Name(), Version: fakeOtherRelease.Version(), }, })) }) Context("when one of the releases in the deployment has changed", func() { JustBeforeEach(func() { olderReleaseVersion := "1233" Expect(fakeOtherRelease.Version()).ToNot(Equal(olderReleaseVersion)) previousDeploymentState := biconfig.DeploymentState{ DirectorID: directorID, CurrentReleaseIDs: []string{"existing-release-id-1", "existing-release-id-2"}, Releases: []biconfig.ReleaseRecord{ { ID: "existing-release-id-1", Name: fakeCPIRelease.Name(), Version: fakeCPIRelease.Version(), }, { ID: "existing-release-id-2", Name: fakeOtherRelease.Name(), Version: olderReleaseVersion, }, }, CurrentStemcellID: "my-stemcellRecordID", Stemcells: []biconfig.StemcellRecord{{ ID: "my-stemcellRecordID", Name: cloudStemcell.Name(), Version: cloudStemcell.Version(), }}, CurrentManifestSHA1: manifestSHA1, } err := setupDeploymentStateService.Save(previousDeploymentState) Expect(err).ToNot(HaveOccurred()) }) It("updates the deployment record, clearing out unused releases", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) deploymentState, err := setupDeploymentStateService.Load() Expect(err).ToNot(HaveOccurred()) Expect(deploymentState.CurrentManifestSHA1).To(Equal(manifestSHA1)) keys := []string{} ids := []string{} for _, releaseRecord := range deploymentState.Releases { keys = append(keys, fmt.Sprintf("%s-%s", releaseRecord.Name, releaseRecord.Version)) ids = append(ids, releaseRecord.ID) } Expect(deploymentState.CurrentReleaseIDs).To(ConsistOf(ids)) Expect(keys).To(ConsistOf([]string{ fmt.Sprintf("%s-%s", fakeCPIRelease.Name(), fakeCPIRelease.Version()), fmt.Sprintf("%s-%s", fakeOtherRelease.Name(), fakeOtherRelease.Version()), })) }) }) Context("when the deployment has not changed", func() { JustBeforeEach(func() { previousDeploymentState := biconfig.DeploymentState{ DirectorID: directorID, CurrentReleaseIDs: []string{"my-release-id-1", "my-release-id-2"}, Releases: []biconfig.ReleaseRecord{ { ID: "my-release-id-1", Name: fakeCPIRelease.Name(), Version: fakeCPIRelease.Version(), }, { ID: "my-release-id-2", Name: fakeOtherRelease.Name(), Version: fakeOtherRelease.Version(), }, }, CurrentStemcellID: "my-stemcellRecordID", Stemcells: []biconfig.StemcellRecord{{ ID: "my-stemcellRecordID", Name: cloudStemcell.Name(), Version: cloudStemcell.Version(), }}, CurrentManifestSHA1: manifestSHA1, } err := setupDeploymentStateService.Save(previousDeploymentState) Expect(err).ToNot(HaveOccurred()) }) It("skips deploy", func() { expectDeploy.Times(0) err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) Expect(stdOut).To(gbytes.Say("No deployment, stemcell or release changes. Skipping deploy.")) }) }) }) Context("when release name does not match the name in release tarball", func() { BeforeEach(func() { releaseSetManifest.Releases = []birelmanifest.ReleaseRef{ { Name: "fake-other-cpi-release-name", URL: "file://" + cpiReleaseTarballPath, }, } }) It("returns an error", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Release name 'fake-other-cpi-release-name' does not match the name in release tarball 'fake-cpi-release-name'")) }) }) Context("When the stemcell tarball does not exist", func() { JustBeforeEach(func() { fakeStemcellExtractor.SetExtractBehavior(stemcellTarballPath, extractedStemcell, errors.New("no-stemcell-there")) }) It("returns error", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("no-stemcell-there")) performCall := fakeStage.PerformCalls[0].Stage.PerformCalls[3] Expect(performCall.Name).To(Equal("Validating stemcell")) Expect(performCall.Error.Error()).To(ContainSubstring("no-stemcell-there")) }) }) Context("when release file does not exist", func() { BeforeEach(func() { mockReleaseExtractor.EXPECT().Extract(cpiReleaseTarballPath).Return(nil, errors.New("not there")) }) It("returns error", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("not there")) performCall := fakeStage.PerformCalls[0].Stage.PerformCalls[0] Expect(performCall.Name).To(Equal("Validating release 'fake-cpi-release-name'")) Expect(performCall.Error.Error()).To(ContainSubstring("not there")) }) }) Context("when the deployment state file does not exist", func() { BeforeEach(func() { fakeFs.RemoveAll(deploymentStatePath) }) It("creates a deployment state", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).ToNot(HaveOccurred()) deploymentState, err := setupDeploymentStateService.Load() Expect(err).ToNot(HaveOccurred()) Expect(deploymentState.DirectorID).To(Equal(directorID)) }) }) It("returns err when the deployment manifest does not exist", func() { fakeFs.RemoveAll(deploymentManifestPath) err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Deployment manifest does not exist at '/path/to/manifest.yml'")) Expect(stdErr).To(gbytes.Say("Deployment '/path/to/manifest.yml' does not exist")) }) Context("when the deployment manifest is invalid", func() { BeforeEach(func() { fakeDeploymentValidator.SetValidateBehavior([]fakebideplval.ValidateOutput{ {Err: bosherr.Error("fake-deployment-validation-error")}, }) }) It("returns err", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-deployment-validation-error")) }) It("logs the failed event log", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) performCall := fakeStage.PerformCalls[0].Stage.PerformCalls[2] Expect(performCall.Name).To(Equal("Validating deployment manifest")) Expect(performCall.Error.Error()).To(Equal("Validating deployment manifest: fake-deployment-validation-error")) }) }) Context("when validating jobs fails", func() { BeforeEach(func() { fakeDeploymentValidator.SetValidateReleaseJobsBehavior([]fakebideplval.ValidateReleaseJobsOutput{ {Err: bosherr.Error("fake-jobs-validation-error")}, }) }) It("returns err", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-jobs-validation-error")) }) It("logs the failed event log", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) performCall := fakeStage.PerformCalls[0].Stage.PerformCalls[2] Expect(performCall.Name).To(Equal("Validating deployment manifest")) Expect(performCall.Error.Error()).To(Equal("Validating deployment jobs refer to jobs in release: fake-jobs-validation-error")) }) }) It("returns err when number of arguments is not equal 1", func() { err := command.Run(fakeStage, []string{}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Invalid usage")) err = command.Run(fakeStage, []string{"1", "2"}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Invalid usage")) }) Context("when uploading stemcell fails", func() { JustBeforeEach(func() { expectStemcellUpload.Return(nil, bosherr.Error("fake-upload-error")) }) It("returns an error", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-upload-error")) }) }) Context("when deploy fails", func() { BeforeEach(func() { mockDeployer.EXPECT().Deploy( cloud, boshDeploymentManifest, cloudStemcell, installationManifest.Registry, fakeVMManager, mockBlobstore, gomock.Any(), ).Return(nil, errors.New("fake-deploy-error")).AnyTimes() previousDeploymentState := biconfig.DeploymentState{ CurrentReleaseIDs: []string{"my-release-id-1"}, Releases: []biconfig.ReleaseRecord{{ ID: "my-release-id-1", Name: fakeCPIRelease.Name(), Version: fakeCPIRelease.Version(), }}, CurrentManifestSHA1: "fake-manifest-sha", } setupDeploymentStateService.Save(previousDeploymentState) }) It("clears the deployment record", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-deploy-error")) deploymentState, err := setupDeploymentStateService.Load() Expect(err).ToNot(HaveOccurred()) Expect(deploymentState.CurrentManifestSHA1).To(Equal("")) Expect(deploymentState.Releases).To(Equal([]biconfig.ReleaseRecord{})) Expect(deploymentState.CurrentReleaseIDs).To(Equal([]string{})) }) }) Context("when compiled releases are being used", func() { var ( otherReleaseTarballPath string fakeOtherRelease *fakebirel.FakeRelease expectOtherReleaseExtract *gomock.Call ) BeforeEach(func() { otherReleaseTarballPath = "/path/to/other-release.tgz" fakeFs.WriteFileString(otherReleaseTarballPath, "") fakeOtherRelease = fakebirel.New("other-release", "1234") fakeOtherRelease.ReleaseIsCompiled = true fakeOtherRelease.ReleaseJobs = []bireljob.Job{{Name: "not-cpi"}} fakeOtherRelease.ReleasePackages = []*bipkg.Package{ { Stemcell: "ubuntu-trusty/fake-stemcell-version", }, } expectOtherReleaseExtract = mockReleaseExtractor.EXPECT().Extract( otherReleaseTarballPath, ).Return(fakeOtherRelease, nil).AnyTimes() releaseSetManifest = birelsetmanifest.Manifest{ Releases: []birelmanifest.ReleaseRef{ { Name: "fake-cpi-release-name", URL: "file://" + cpiReleaseTarballPath, }, { Name: "other-release", URL: "file://" + otherReleaseTarballPath, }, }, } boshDeploymentManifest = bideplmanifest.Manifest{ Name: "fake-deployment-name", Jobs: []bideplmanifest.Job{ { Name: "fake-job-name", Templates: []bideplmanifest.ReleaseJobRef{ { Release: "other-release", }, }, }, }, ResourcePools: []bideplmanifest.ResourcePool{ { Stemcell: bideplmanifest.StemcellRef{ URL: "file://" + stemcellTarballPath, }, }, }, } }) It("extracts the compiled release tarball", func() { expectOtherReleaseExtract.Times(1) err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).NotTo(HaveOccurred()) }) It("parse compiled releases correctly", func() { err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).ToNot(HaveOccurred()) }) It("returns error if compiled package stemcell does not match the deployment stemcell", func() { fakeOtherRelease.ReleasePackages = []*bipkg.Package{ { Stemcell: "ubuntu-trusty/wrong-version", }, } err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("OS/Version mismatch between deployment stemcell and compiled package stemcell for release 'other-release'")) }) It("returns error if CPI release is compiled", func() { fakeCPIRelease.ReleaseIsCompiled = true err := command.Run(fakeStage, []string{deploymentManifestPath}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("CPI is not allowed to be a compiled release. The provided CPI release 'fake-cpi-release-name' is compiled")) }) }) }) }
fixtureSHA1 = "da39a3ee5e6b4b0d3255bfef95601890afd80709" fixtureSHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" fixtureSHA512 = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e" ) var ( innerBlobstore *fakeblob.FakeBlobstore checksumVerifiableBlobstore boshblob.Blobstore checksumProvider boshcrypto.DigestProvider fixtureDigest boshcrypto.Digest ) BeforeEach(func() { fixtureDigest = boshcrypto.NewDigest(boshcrypto.DigestAlgorithmSHA1, fixtureSHA1) innerBlobstore = &fakeblob.FakeBlobstore{} checksumProvider = boshcrypto.NewDigestProvider(fakesys.NewFakeFileSystem()) checksumVerifiableBlobstore = boshblob.NewDigestVerifiableBlobstore(innerBlobstore, checksumProvider) }) Describe("Get", func() { It("returns without an error if sha1 matches", func() { innerBlobstore.GetFileName = fixturePath fileName, err := checksumVerifiableBlobstore.Get("fake-blob-id", fixtureDigest) Expect(err).ToNot(HaveOccurred()) Expect(innerBlobstore.GetBlobIDs).To(Equal([]string{"fake-blob-id"})) Expect(fileName).To(Equal(fixturePath)) }) It("returns error if sha1 does not match", func() {
fakeboshsys "github.com/cloudfoundry/bosh-utils/system/fakes" ) func expectedLogFormat(tag, msg string) string { return fmt.Sprintf("\\[%s\\] [0-9]{4}/[0-9]{2}/[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} %s\n", tag, msg) } var _ = Describe("NewFileLogger", func() { var ( fs *fakeboshsys.FakeFileSystem logFile boshsys.File ) BeforeEach(func() { fs = fakeboshsys.NewFakeFileSystem() var err error logFile, err = fs.TempFile("file-logger-test") Expect(err).ToNot(HaveOccurred()) err = logFile.Close() Expect(err).ToNot(HaveOccurred()) }) AfterEach(func() { logFile.Close() fs.RemoveAll(logFile.Name()) }) It("logs the formatted DEBUG message to the file", func() { logger, logFile, err := New(boshlog.LevelDebug, logFile.Name(), DefaultLogFileMode, fs) Expect(err).ToNot(HaveOccurred())
func describeDummyPlatform() { var ( platform Platform collector boshstats.Collector fs *fakesys.FakeFileSystem cmdRunner boshsys.CmdRunner dirProvider boshdirs.Provider devicePathResolver boshdpresolv.DevicePathResolver logger boshlog.Logger ) BeforeEach(func() { collector = &fakestats.FakeCollector{} fs = fakesys.NewFakeFileSystem() cmdRunner = fakesys.NewFakeCmdRunner() dirProvider = boshdirs.NewProvider("/fake-dir") devicePathResolver = fakedpresolv.NewFakeDevicePathResolver() logger = boshlog.NewLogger(boshlog.LevelNone) }) JustBeforeEach(func() { platform = NewDummyPlatform( collector, fs, cmdRunner, dirProvider, devicePathResolver, logger, ) }) Describe("GetDefaultNetwork", func() { It("returns the contents of dummy-defaults-network-settings.json since that's what the dummy cpi writes", func() { settingsFilePath := "/fake-dir/bosh/dummy-default-network-settings.json" fs.WriteFileString(settingsFilePath, `{"IP": "1.2.3.4"}`) network, err := platform.GetDefaultNetwork() Expect(err).NotTo(HaveOccurred()) Expect(network.IP).To(Equal("1.2.3.4")) }) }) Describe("GetCertManager", func() { It("returs a dummy cert manager", func() { certManager := platform.GetCertManager() Expect(certManager.UpdateCertificates("")).Should(BeNil()) }) }) Describe("UnmountPersistentDisk", func() { Context("when there are two mounted persistent disks in the mounts json", func() { BeforeEach(func() { var mounts []mount mounts = append(mounts, mount{MountDir: "dir1", DiskCid: "cid1"}) mounts = append(mounts, mount{MountDir: "dir2", DiskCid: "cid2"}) mountsJSON, _ := json.Marshal(mounts) mountsPath := path.Join(dirProvider.BoshDir(), "mounts.json") fs.WriteFile(mountsPath, mountsJSON) }) It("removes one of the disks from the mounts json", func() { unmounted, err := platform.UnmountPersistentDisk(settings.DiskSettings{ID: "cid1"}) Expect(err).NotTo(HaveOccurred()) Expect(unmounted).To(Equal(true)) _, isMountPoint, err := platform.IsMountPoint("dir1") Expect(isMountPoint).To(Equal(false)) _, isMountPoint, err = platform.IsMountPoint("dir2") Expect(isMountPoint).To(Equal(true)) }) }) }) Describe("SetUserPassword", func() { It("writes the password to a file", func() { err := platform.SetUserPassword("user-name", "fake-password") Expect(err).NotTo(HaveOccurred()) userPasswordsPath := path.Join(dirProvider.BoshDir(), "user-name", CredentialFileName) password, err := fs.ReadFileString(userPasswordsPath) Expect(err).NotTo(HaveOccurred()) Expect(password).To(Equal("fake-password")) }) It("writes the passwords to different files for each user", func() { err := platform.SetUserPassword("user-name1", "fake-password1") Expect(err).NotTo(HaveOccurred()) err = platform.SetUserPassword("user-name2", "fake-password2") Expect(err).NotTo(HaveOccurred()) userPasswordsPath := path.Join(dirProvider.BoshDir(), "user-name1", CredentialFileName) password, err := fs.ReadFileString(userPasswordsPath) Expect(err).NotTo(HaveOccurred()) Expect(password).To(Equal("fake-password1")) userPasswordsPath = path.Join(dirProvider.BoshDir(), "user-name2", CredentialFileName) password, err = fs.ReadFileString(userPasswordsPath) Expect(err).NotTo(HaveOccurred()) Expect(password).To(Equal("fake-password2")) }) }) Describe("SetupDataDir", func() { It("creates a link from BASEDIR/sys to BASEDIR/data/sys", func() { err := platform.SetupDataDir() Expect(err).NotTo(HaveOccurred()) stat := fs.GetFileTestStat("/fake-dir/sys") Expect(stat).ToNot(BeNil()) Expect(stat.SymlinkTarget).To(Equal("/fake-dir/data/sys")) }) }) }
Expect(err).ToNot(HaveOccurred()) Expect(1).To(Equal(len(cmdRunner.RunCommands))) Expect(cmdRunner.RunCommands[0]).To(Equal( []string{ "tar", "--same-owner", "-xzvf", tarballPath, "-C", dstDir, }, )) }) }) Describe("CleanUp", func() { It("removes tarball path", func() { fs := fakesys.NewFakeFileSystem() compressor := NewTarballCompressor(cmdRunner, fs) err := fs.WriteFileString("/fake-tarball.tar", "") Expect(err).ToNot(HaveOccurred()) err = compressor.CleanUp("/fake-tarball.tar") Expect(err).ToNot(HaveOccurred()) Expect(fs.FileExists("/fake-tarball.tar")).To(BeFalse()) }) It("returns error if removing tarball path fails", func() { fs := fakesys.NewFakeFileSystem() compressor := NewTarballCompressor(cmdRunner, fs)
func init() { Describe("compiledPackageApplier", func() { var ( packagesBc *fakebc.FakeBundleCollection blobstore *fakeblob.FakeBlobstore compressor *fakecmd.FakeCompressor fs *fakesys.FakeFileSystem logger boshlog.Logger applier Applier ) BeforeEach(func() { packagesBc = fakebc.NewFakeBundleCollection() blobstore = fakeblob.NewFakeBlobstore() compressor = fakecmd.NewFakeCompressor() fs = fakesys.NewFakeFileSystem() logger = boshlog.NewLogger(boshlog.LevelNone) applier = NewCompiledPackageApplier(packagesBc, true, blobstore, compressor, fs, logger) }) Describe("Prepare & Apply", func() { var ( pkg models.Package bundle *fakebc.FakeBundle ) BeforeEach(func() { pkg, bundle = buildPkg(packagesBc) }) ItInstallsPkg := func(act func() error) { It("returns error when installing package 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 package 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 package 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 package blob to tmp path and later cleans it up", func() { fs.TempDirDir = "/fake-tmp-dir" 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 package blob fails", func() { compressor.DecompressFileToDirErr = errors.New("fake-decompress-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-decompress-error")) }) It("installs bundle from decompressed tmp path of a package blob", func() { fs.TempDirDir = "/fake-tmp-dir" 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")) }) } Describe("Prepare", func() { act := func() error { return applier.Prepare(pkg) } It("return an error if getting file bundle fails", func() { packagesBc.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 package installation 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 package 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 package", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(blobstore.GetBlobIDs).To(BeNil()) }) }) Context("when package is not installed", func() { BeforeEach(func() { bundle.Installed = false }) It("installs package (but does not enable it)", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(bundle.ActionsCalled).To(Equal([]string{"Install"})) }) ItInstallsPkg(act) }) }) Describe("Apply", func() { act := func() error { return applier.Apply(pkg) } It("return an error if getting file bundle fails", func() { packagesBc.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 package installation 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 package is already installed", func() { BeforeEach(func() { bundle.Installed = true }) It("does not install but only enables package", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(bundle.ActionsCalled).To(Equal([]string{"Enable"})) // no Install }) It("returns error when package 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 package", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(blobstore.GetBlobIDs).To(BeNil()) }) }) Context("when package is not installed", func() { BeforeEach(func() { bundle.Installed = false }) It("installs and enables package", func() { err := act() Expect(err).ToNot(HaveOccurred()) Expect(bundle.ActionsCalled).To(Equal([]string{"Install", "Enable"})) }) It("returns error when package enable fails", func() { bundle.EnableError = errors.New("fake-enable-error") err := act() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-enable-error")) }) ItInstallsPkg(act) }) }) }) Describe("KeepOnly", func() { ItReturnsErrors := func() { It("returns error when bundle collection fails to return list of installed bundles", func() { packagesBc.ListErr = errors.New("fake-bc-list-error") err := applier.KeepOnly([]models.Package{}) 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 package", func() { pkg1, bundle1 := buildPkg(packagesBc) packagesBc.ListBundles = []boshbc.Bundle{bundle1} packagesBc.GetErr = errors.New("fake-bc-get-error") err := applier.KeepOnly([]models.Package{pkg1}) 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 := buildPkg(packagesBc) packagesBc.ListBundles = []boshbc.Bundle{bundle1} bundle1.DisableErr = errors.New("fake-bc-disable-error") err := applier.KeepOnly([]models.Package{}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-bc-disable-error")) }) } Context("when operating on packages as a package owner", func() { BeforeEach(func() { applier = NewCompiledPackageApplier(packagesBc, true, blobstore, compressor, fs, logger) }) It("first disables and then uninstalls packages that are not in keeponly list", func() { _, bundle1 := buildPkg(packagesBc) pkg2, bundle2 := buildPkg(packagesBc) _, bundle3 := buildPkg(packagesBc) pkg4, bundle4 := buildPkg(packagesBc) packagesBc.ListBundles = []boshbc.Bundle{bundle1, bundle2, bundle3, bundle4} err := applier.KeepOnly([]models.Package{pkg4, pkg2}) 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{})) }) ItReturnsErrors() It("returns error when at least one bundle cannot be uninstalled", func() { _, bundle1 := buildPkg(packagesBc) packagesBc.ListBundles = []boshbc.Bundle{bundle1} bundle1.UninstallErr = errors.New("fake-bc-uninstall-error") err := applier.KeepOnly([]models.Package{}) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-bc-uninstall-error")) }) }) Context("when operating on packages not as a package owner", func() { BeforeEach(func() { applier = NewCompiledPackageApplier(packagesBc, false, blobstore, compressor, fs, logger) }) It("disables and but does not uninstall packages that are not in keeponly list", func() { _, bundle1 := buildPkg(packagesBc) pkg2, bundle2 := buildPkg(packagesBc) _, bundle3 := buildPkg(packagesBc) pkg4, bundle4 := buildPkg(packagesBc) packagesBc.ListBundles = []boshbc.Bundle{bundle1, bundle2, bundle3, bundle4} err := applier.KeepOnly([]models.Package{pkg4, pkg2}) Expect(err).ToNot(HaveOccurred()) Expect(bundle1.ActionsCalled).To(Equal([]string{"Disable"})) // no Uninstall Expect(bundle2.ActionsCalled).To(Equal([]string{})) Expect(bundle3.ActionsCalled).To(Equal([]string{"Disable"})) // no Uninstall Expect(bundle4.ActionsCalled).To(Equal([]string{})) }) ItReturnsErrors() }) }) }) }
err := blobManager.Write(blobId, toWrite) Expect(err).ToNot(HaveOccurred()) contents, err := fs.ReadFileString(blobPath) Expect(err).ToNot(HaveOccurred()) Expect(contents).To(Equal("new data")) }) Context("when it writes", func() { BeforeEach(func() { basePath = filepath.ToSlash(basePath) blobPath = filepath.ToSlash(blobPath) }) It("creates and closes the file", func() { fs_ := boshsysfake.NewFakeFileSystem() blobManager := NewBlobManager(fs_, basePath) err := blobManager.Write(blobId, toWrite) Expect(err).ToNot(HaveOccurred()) fileStats, err := fs_.FindFileStats(blobPath) Expect(err).ToNot(HaveOccurred()) Expect(fileStats.Open).To(BeFalse()) }) It("creates file with correct permissions", func() { fs_ := boshsysfake.NewFakeFileSystem() blobManager := NewBlobManager(fs_, basePath) err := blobManager.Write(blobId, toWrite) fileStats, err := fs_.FindFileStats(blobPath) Expect(err).ToNot(HaveOccurred()) Expect(fileStats.FileMode).To(Equal(os.FileMode(0666)))
func describeCentosNetManager() { var ( fs *fakesys.FakeFileSystem cmdRunner *fakesys.FakeCmdRunner ipResolver *fakeip.FakeResolver interfaceAddrsProvider *fakeip.FakeInterfaceAddressesProvider 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) interfaceAddrsProvider = &fakeip.FakeInterfaceAddressesProvider{} interfaceAddrsValidator := boship.NewInterfaceAddressesValidator(interfaceAddrsProvider) dnsValidator := NewDNSValidator(fs) addressBroadcaster = &fakearp.FakeAddressBroadcaster{} netManager = NewCentosNetManager( fs, cmdRunner, ipResolver, interfaceConfigurationCreator, interfaceAddrsValidator, dnsValidator, 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", } interfaceAddrsProvider.GetInterfaceAddresses = []boship.InterfaceAddress{ boship.NewSimpleInterfaceAddress("ethstatic", "1.2.3.4"), } fs.WriteFileString("/etc/resolv.conf", ` nameserver 8.8.8.8 nameserver 9.9.9.9 `) 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, }) interfaceAddrsProvider.GetInterfaceAddresses = []boship.InterfaceAddress{ boship.NewSimpleInterfaceAddress("ethstatic", "1.2.3.4"), boship.NewSimpleInterfaceAddress("ethstatic-that-changes", "1.2.3.5"), } 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"})) }) Context("when manual networks were not configured with proper IP addresses", func() { BeforeEach(func() { interfaceAddrsProvider.GetInterfaceAddresses = []boship.InterfaceAddress{ boship.NewSimpleInterfaceAddress("ethstatic", "1.2.3.5"), } }) It("fails", func() { stubInterfaces(map[string]boshsettings.Network{ "ethstatic": staticNetwork, }) errCh := make(chan error) err := netManager.SetupNetworking(boshsettings.Networks{"static-network": staticNetwork}, errCh) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Validating static network configuration")) }) }) Context("when dns is not properly configured", func() { BeforeEach(func() { fs.WriteFileString("/etc/resolv.conf", "") }) It("fails", func() { staticNetwork = boshsettings.Network{ Type: "manual", IP: "1.2.3.4", Default: []string{"dns"}, DNS: []string{"8.8.8.8"}, Netmask: "255.255.255.0", Gateway: "3.4.5.6", Mac: "fake-static-mac-address", } stubInterfaces(map[string]boshsettings.Network{ "ethstatic": staticNetwork, }) errCh := make(chan error) err := netManager.SetupNetworking(boshsettings.Networks{"static-network": staticNetwork}, errCh) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Validating dns configuration")) }) }) 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("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.RemoveAllStub = func(path string) error { if path == "/fake-compile-dir/pkg_name" { return errors.New("fake-remove-error") } return nil } _, _, 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.RemoveAllStub = func(path string) error { if path == "/fake-compile-dir/pkg_name" { return errors.New("fake-mkdir-error") } return nil } _, _, 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.RemoveAllStub = func(path string) error { if path == "/fake-compile-dir/pkg_name-bosh-agent-unpack" { return errors.New("fake-remove-error") } return nil } _, _, 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", })) }) It("returns an error if removing the compile directory fails", func() { callCount := 0 fs.RemoveAllStub = func(path string) error { if path == "/fake-compile-dir/pkg_name" { callCount++ if callCount > 1 { return errors.New("fake-remove-error") } } return nil } _, _, err := compiler.Compile(pkg, pkgDeps) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-remove-error")) }) Context("when packaging script exists", func() { const packagingScriptContents = "hi" BeforeEach(func() { compressor.DecompressFileToDirCallBack = func() { filename := "/fake-compile-dir/pkg_name/" + PackagingScriptName fs.WriteFileString(filename, packagingScriptContents) } }) It("runs packaging script ", func() { _, _, err := compiler.Compile(pkg, pkgDeps) Expect(err).ToNot(HaveOccurred()) expectedCmd := boshsys.Command{ 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", } cmd := runner.RunCommands[0] if runtime.GOOS == "windows" { expectedCmd.Name = "powershell" expectedCmd.Args = []string{"-command", fmt.Sprintf(`"iex (get-content -raw %s)"`, PackagingScriptName)} } else { expectedCmd.Name = "bash" expectedCmd.Args = []string{"-x", PackagingScriptName} } Expect(cmd).To(Equal(expectedCmd)) Expect(len(runner.RunCommands)).To(Equal(1)) Expect(runner.RunCommandJobName).To(Equal("compilation")) Expect(runner.RunCommandTaskName).To(Equal(PackagingScriptName)) }) 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")) }) }) }) }
func describeDrbd() { var ( dualDCSupport *DualDCSupport cmdRunner *fakesys.FakeCmdRunner fs *fakesys.FakeFileSystem dirProvider boshdir.Provider specService *fakeas.FakeV1Service settingsService *fakesettings.FakeSettingsService logger boshlog.Logger spec boshas.V1ApplySpec ) BeforeEach(func() { fs = fakesys.NewFakeFileSystem() cmdRunner = fakesys.NewFakeCmdRunner() specService = fakeas.NewFakeV1Service() settingsService = &fakesettings.FakeSettingsService{} dirProvider = boshdir.NewProvider("/var/vcap") logger = boshlog.NewLogger(boshlog.LevelNone) dualDCSupport = NewDualDCSupport( cmdRunner, fs, dirProvider, specService, settingsService, logger, ) spec = boshas.V1ApplySpec{} spec.DrbdEnabled = true spec.Passive = "enabled" specService.Spec = spec }) Context("DNS updates", func() { It("", func() { s, e := specService.Get() fmt.Printf("spec: %#v, err: %v", s, e) }) }) Describe("Drbd", func() { It("renders config file", func() { expectedOutput := ` resource r0 { net { protocol A; shared-secret OIUncfjJsbhInuic1243d; verify-alg sha1; } disk { resync-rate 24M; } handlers { before-resync-target "/lib/drbd/snapshot-resync-target-lvm.sh"; after-resync-target "/lib/drbd/unsnapshot-resync-target-lvm.sh"; } startup { wfc-timeout 3; degr-wfc-timeout 3; outdated-wfc-timeout 2; } on dff85535-580a-4bfb-bf49-5efbc017b5bb { device drbd1; disk /dev/mapper/vgStoreData-StoreData; address 10.76.245.71:7789; meta-disk internal; } on host2 { device drbd1; disk /dev/mapper/vgStoreData-StoreData; address 10.92.245.71:7789; meta-disk internal; } } ` // TODO: fix the test - sort out the fakes // out := drbdConfig("A", "OIUncfjJsbhInuic1243d", "dff85535-580a-4bfb-bf49-5efbc017b5bb", "10.76.245.71", "10.92.245.71") Expect("").NotTo(Equal(expectedOutput)) }) }) }
var validActionsOptions = bslcaction.ConcreteFactoryOptions{ StemcellsDir: "/tmp/stemcells", } var validConfig = Config{ SoftLayer: validSoftLayerConfig, Actions: validActionsOptions, } var _ = Describe("NewConfigFromPath", func() { var ( fs *fakesys.FakeFileSystem ) BeforeEach(func() { fs = fakesys.NewFakeFileSystem() }) It("returns error if config is not valid", func() { err := fs.WriteFileString("/config.json", "{}") Expect(err).ToNot(HaveOccurred()) _, err = NewConfigFromPath("/config.json", fs) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Validating config")) }) It("returns error if file contains invalid json", func() { err := fs.WriteFileString("/config.json", "-") Expect(err).ToNot(HaveOccurred())
func describeDummyPlatform() { var ( platform Platform collector boshstats.Collector fs boshsys.FileSystem cmdRunner boshsys.CmdRunner dirProvider boshdirs.Provider devicePathResolver boshdpresolv.DevicePathResolver logger boshlog.Logger ) BeforeEach(func() { collector = &fakestats.FakeCollector{} fs = fakesys.NewFakeFileSystem() cmdRunner = fakesys.NewFakeCmdRunner() dirProvider = boshdirs.NewProvider("/fake-dir") devicePathResolver = fakedpresolv.NewFakeDevicePathResolver() logger = boshlog.NewLogger(boshlog.LevelNone) }) JustBeforeEach(func() { platform = NewDummyPlatform( collector, fs, cmdRunner, dirProvider, devicePathResolver, logger, ) }) Describe("GetDefaultNetwork", func() { It("returns the contents of dummy-defaults-network-settings.json since that's what the dummy cpi writes", func() { settingsFilePath := "/fake-dir/bosh/dummy-default-network-settings.json" fs.WriteFileString(settingsFilePath, `{"IP": "1.2.3.4"}`) network, err := platform.GetDefaultNetwork() Expect(err).NotTo(HaveOccurred()) Expect(network.IP).To(Equal("1.2.3.4")) }) }) Describe("GetCertManager", func() { It("returs a dummy cert manager", func() { certManager := platform.GetCertManager() Expect(certManager.UpdateCertificates("")).Should(BeNil()) }) }) Describe("UnmountPersistentDisk", func() { Context("when there are two mounted persistent disks in the mounts json", func() { BeforeEach(func() { var mounts []mount mounts = append(mounts, mount{MountDir: "dir1", DiskCid: "cid1"}) mounts = append(mounts, mount{MountDir: "dir2", DiskCid: "cid2"}) mountsJSON, _ := json.Marshal(mounts) mountsPath := path.Join(dirProvider.BoshDir(), "mounts.json") fs.WriteFile(mountsPath, mountsJSON) }) It("removes one of the disks from the mounts json", func() { unmounted, err := platform.UnmountPersistentDisk(settings.DiskSettings{ID: "cid1"}) Expect(err).NotTo(HaveOccurred()) Expect(unmounted).To(Equal(true)) Expect(platform.IsMountPoint("dir1")).To(Equal(false)) Expect(platform.IsMountPoint("dir2")).To(Equal(true)) }) }) }) }