func newSignalableFileLogger(logPath string, level boshlog.LogLevel) boshlog.Logger { // Log file logger errors to the STDERR logger logger := boshlog.NewLogger(boshlog.LevelError) fileSystem := boshsys.NewOsFileSystem(logger) // log file will be closed by process exit // log file readable by all logfileLogger, _, err := boshlogfile.New(level, logPath, boshlogfile.DefaultLogFileMode, fileSystem) if err != nil { logger := boshlog.NewLogger(boshlog.LevelError) ui := biui.NewConsoleUI(logger) fail(err, ui, logger, nil) } return newSignalableLogger(logfileLogger) }
func newLogger() boshlog.Logger { logLevelString := os.Getenv("BOSH_INIT_LOG_LEVEL") level := boshlog.LevelNone if logLevelString != "" { var err error level, err = boshlog.Levelify(logLevelString) if err != nil { err = bosherr.WrapError(err, "Invalid BOSH_INIT_LOG_LEVEL value") logger := boshlog.NewLogger(boshlog.LevelError) ui := biui.NewConsoleUI(logger) fail(err, ui, logger, nil) } } logPath := os.Getenv("BOSH_INIT_LOG_PATH") if logPath != "" { return newSignalableFileLogger(logPath, level) } return newSignalableLogger(boshlog.NewLogger(level)) }
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 describeHTTPMetadataService() { var ( dnsResolver *fakeinf.FakeDNSResolver platform *fakeplat.FakePlatform logger boshlog.Logger metadataService MetadataService ) BeforeEach(func() { dnsResolver = &fakeinf.FakeDNSResolver{} platform = fakeplat.NewFakePlatform() logger = boshlog.NewLogger(boshlog.LevelNone) metadataService = NewHTTPMetadataService("fake-metadata-host", dnsResolver, platform, logger) }) ItEnsuresMinimalNetworkSetup := func(subject func() (string, error)) { Context("when no networks are configured", func() { BeforeEach(func() { platform.GetConfiguredNetworkInterfacesInterfaces = []string{} }) It("sets up DHCP network", func() { _, err := subject() Expect(err).ToNot(HaveOccurred()) Expect(platform.SetupNetworkingCalled).To(BeTrue()) Expect(platform.SetupNetworkingNetworks).To(Equal(boshsettings.Networks{ "eth0": boshsettings.Network{ Type: "dynamic", }, })) }) Context("when setting up DHCP fails", func() { BeforeEach(func() { platform.SetupNetworkingErr = errors.New("fake-network-error") }) It("returns an error", func() { _, err := subject() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-network-error")) }) }) }) } Describe("IsAvailable", func() { It("returns true", func() { Expect(metadataService.IsAvailable()).To(BeTrue()) }) }) Describe("GetPublicKey", func() { var ( ts *httptest.Server ) BeforeEach(func() { handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { GinkgoRecover() Expect(r.Method).To(Equal("GET")) Expect(r.URL.Path).To(Equal("/latest/meta-data/public-keys/0/openssh-key")) w.Write([]byte("fake-public-key")) }) ts = httptest.NewServer(handler) metadataService = NewHTTPMetadataService(ts.URL, dnsResolver, platform, logger) }) AfterEach(func() { ts.Close() }) ItEnsuresMinimalNetworkSetup(func() (string, error) { return metadataService.GetPublicKey() }) It("returns fetched public key", func() { publicKey, err := metadataService.GetPublicKey() Expect(err).NotTo(HaveOccurred()) Expect(publicKey).To(Equal("fake-public-key")) }) }) Describe("GetInstanceID", func() { var ( ts *httptest.Server ) BeforeEach(func() { handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { GinkgoRecover() Expect(r.Method).To(Equal("GET")) Expect(r.URL.Path).To(Equal("/latest/meta-data/instance-id")) w.Write([]byte("fake-instance-id")) }) ts = httptest.NewServer(handler) metadataService = NewHTTPMetadataService(ts.URL, dnsResolver, platform, logger) }) AfterEach(func() { ts.Close() }) ItEnsuresMinimalNetworkSetup(func() (string, error) { return metadataService.GetInstanceID() }) It("returns fetched instance id", func() { publicKey, err := metadataService.GetInstanceID() Expect(err).NotTo(HaveOccurred()) Expect(publicKey).To(Equal("fake-instance-id")) }) }) Describe("GetServerName", func() { var ( ts *httptest.Server serverName *string ) handlerFunc := func(w http.ResponseWriter, r *http.Request) { GinkgoRecover() Expect(r.Method).To(Equal("GET")) Expect(r.URL.Path).To(Equal("/latest/user-data")) var jsonStr string if serverName == nil { jsonStr = `{}` } else { jsonStr = fmt.Sprintf(`{"server":{"name":"%s"}}`, *serverName) } w.Write([]byte(jsonStr)) } BeforeEach(func() { serverName = nil handler := http.HandlerFunc(handlerFunc) ts = httptest.NewServer(handler) metadataService = NewHTTPMetadataService(ts.URL, dnsResolver, platform, logger) }) AfterEach(func() { ts.Close() }) Context("when the server name is present in the JSON", func() { BeforeEach(func() { name := "fake-server-name" serverName = &name }) It("returns the server name", func() { name, err := metadataService.GetServerName() Expect(err).ToNot(HaveOccurred()) Expect(name).To(Equal("fake-server-name")) }) ItEnsuresMinimalNetworkSetup(func() (string, error) { return metadataService.GetServerName() }) }) Context("when the server name is not present in the JSON", func() { BeforeEach(func() { serverName = nil }) It("returns an error", func() { name, err := metadataService.GetServerName() Expect(err).To(HaveOccurred()) Expect(name).To(BeEmpty()) }) }) }) Describe("GetRegistryEndpoint", func() { var ( ts *httptest.Server registryURL *string dnsServer *string ) handlerFunc := func(w http.ResponseWriter, r *http.Request) { GinkgoRecover() Expect(r.Method).To(Equal("GET")) Expect(r.URL.Path).To(Equal("/latest/user-data")) var jsonStr string if dnsServer == nil { jsonStr = fmt.Sprintf(`{"registry":{"endpoint":"%s"}}`, *registryURL) } else { jsonStr = fmt.Sprintf(`{ "registry":{"endpoint":"%s"}, "dns":{"nameserver":["%s"]} }`, *registryURL, *dnsServer) } w.Write([]byte(jsonStr)) } BeforeEach(func() { url := "http://fake-registry.com" registryURL = &url dnsServer = nil handler := http.HandlerFunc(handlerFunc) ts = httptest.NewServer(handler) metadataService = NewHTTPMetadataService(ts.URL, dnsResolver, platform, logger) }) AfterEach(func() { ts.Close() }) ItEnsuresMinimalNetworkSetup(func() (string, error) { return metadataService.GetRegistryEndpoint() }) Context("when metadata contains a dns server", func() { BeforeEach(func() { server := "fake-dns-server-ip" dnsServer = &server }) Context("when registry endpoint is successfully resolved", func() { BeforeEach(func() { dnsResolver.RegisterRecord(fakeinf.FakeDNSRecord{ DNSServers: []string{"fake-dns-server-ip"}, Host: "http://fake-registry.com", IP: "http://fake-registry-ip", }) }) It("returns the successfully resolved registry endpoint", func() { endpoint, err := metadataService.GetRegistryEndpoint() Expect(err).ToNot(HaveOccurred()) Expect(endpoint).To(Equal("http://fake-registry-ip")) }) }) Context("when registry endpoint is not successfully resolved", func() { BeforeEach(func() { dnsResolver.LookupHostErr = errors.New("fake-lookup-host-err") }) It("returns error because it failed to resolve registry endpoint", func() { endpoint, err := metadataService.GetRegistryEndpoint() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-lookup-host-err")) Expect(endpoint).To(BeEmpty()) }) }) }) Context("when metadata does not contain dns servers", func() { It("returns fetched registry endpoint", func() { endpoint, err := metadataService.GetRegistryEndpoint() Expect(err).NotTo(HaveOccurred()) Expect(endpoint).To(Equal("http://fake-registry.com")) }) }) }) Describe("GetNetworks", func() { It("returns nil networks, since you don't need them for bootstrapping since your network must be set up before you can get the metadata", func() { Expect(metadataService.GetNetworks()).To(BeNil()) }) }) }
func describeUbuntuNetManager() { var ( fs *fakesys.FakeFileSystem cmdRunner *fakesys.FakeCmdRunner ipResolver *fakeip.FakeResolver addressBroadcaster *fakearp.FakeAddressBroadcaster netManager UbuntuNetManager interfaceConfigurationCreator InterfaceConfigurationCreator ) writeNetworkDevice := func(iface string, macAddress string, isPhysical bool) string { interfacePath := fmt.Sprintf("/sys/class/net/%s", iface) fs.WriteFile(interfacePath, []byte{}) if isPhysical { fs.WriteFile(fmt.Sprintf("/sys/class/net/%s/device", iface), []byte{}) } fs.WriteFileString(fmt.Sprintf("/sys/class/net/%s/address", iface), fmt.Sprintf("%s\n", macAddress)) return interfacePath } stubInterfacesWithVirtual := func(physicalInterfaces map[string]boshsettings.Network, virtualInterfaces []string) { interfacePaths := []string{} for iface, networkSettings := range physicalInterfaces { interfacePaths = append(interfacePaths, writeNetworkDevice(iface, networkSettings.Mac, true)) } for _, iface := range virtualInterfaces { interfacePaths = append(interfacePaths, writeNetworkDevice(iface, "virtual", false)) } fs.SetGlob("/sys/class/net/*", interfacePaths) } stubInterfaces := func(physicalInterfaces map[string]boshsettings.Network) { stubInterfacesWithVirtual(physicalInterfaces, nil) } BeforeEach(func() { fs = fakesys.NewFakeFileSystem() cmdRunner = fakesys.NewFakeCmdRunner() ipResolver = &fakeip.FakeResolver{} logger := boshlog.NewLogger(boshlog.LevelNone) interfaceConfigurationCreator = NewInterfaceConfigurationCreator(logger) addressBroadcaster = &fakearp.FakeAddressBroadcaster{} netManager = NewUbuntuNetManager( fs, cmdRunner, ipResolver, interfaceConfigurationCreator, addressBroadcaster, logger, ).(UbuntuNetManager) }) Describe("ComputeNetworkConfig", func() { Context("when there is one manual network and neither is marked as default for DNS", func() { It("should use the manual network for DNS", func() { networks := boshsettings.Networks{ "manual": factory.Network{DNS: &[]string{"8.8.8.8"}}.Build(), } stubInterfaces(networks) _, _, dnsServers, err := netManager.ComputeNetworkConfig(networks) Expect(err).ToNot(HaveOccurred()) Expect(dnsServers).To(Equal([]string{"8.8.8.8"})) }) }) Context("when there is a vip network and a manual network and neither is marked as default for DNS", func() { It("should use the manual network for DNS", func() { networks := boshsettings.Networks{ "vip": boshsettings.Network{Type: "vip"}, "manual": factory.Network{Type: "manual", DNS: &[]string{"8.8.8.8"}}.Build(), } stubInterfaces(networks) _, _, dnsServers, err := netManager.ComputeNetworkConfig(networks) Expect(err).ToNot(HaveOccurred()) Expect(dnsServers).To(Equal([]string{"8.8.8.8"})) }) }) Context("when there is a vip network and a manual network and the manual network is marked as default for DNS", func() { It("should use the manual network for DNS", func() { networks := boshsettings.Networks{ "vip": boshsettings.Network{Type: "vip"}, "manual": factory.Network{Type: "manual", DNS: &[]string{"8.8.8.8"}, Default: []string{"dns"}}.Build(), } stubInterfaces(networks) _, _, dnsServers, err := netManager.ComputeNetworkConfig(networks) Expect(err).ToNot(HaveOccurred()) Expect(dnsServers).To(Equal([]string{"8.8.8.8"})) }) }) Context("when specified more than one DNS", func() { It("extracts all DNS servers from the network configured as default DNS", func() { networks := boshsettings.Networks{ "default": factory.Network{ IP: "10.10.0.32", Netmask: "255.255.255.0", Mac: "aa::bb::cc", Default: []string{"dns", "gateway"}, DNS: &[]string{"54.209.78.6", "127.0.0.5"}, Gateway: "10.10.0.1", }.Build(), } stubInterfaces(networks) staticInterfaceConfigurations, dhcpInterfaceConfigurations, dnsServers, err := netManager.ComputeNetworkConfig(networks) Expect(err).ToNot(HaveOccurred()) Expect(staticInterfaceConfigurations).To(Equal([]StaticInterfaceConfiguration{ { Name: "default", Address: "10.10.0.32", Netmask: "255.255.255.0", Network: "10.10.0.0", Broadcast: "10.10.0.255", Mac: "aa::bb::cc", Gateway: "10.10.0.1", }, })) Expect(dhcpInterfaceConfigurations).To(BeEmpty()) Expect(dnsServers).To(Equal([]string{"54.209.78.6", "127.0.0.5"})) }) }) }) Describe("SetupNetworking", func() { var ( dhcpNetwork boshsettings.Network staticNetwork boshsettings.Network expectedNetworkConfigurationForStaticAndDhcp string ) BeforeEach(func() { dhcpNetwork = boshsettings.Network{ Type: "dynamic", Default: []string{"dns"}, DNS: []string{"8.8.8.8", "9.9.9.9"}, Mac: "fake-dhcp-mac-address", } staticNetwork = boshsettings.Network{ Type: "manual", IP: "1.2.3.4", Netmask: "255.255.255.0", Gateway: "3.4.5.6", Mac: "fake-static-mac-address", } expectedNetworkConfigurationForStaticAndDhcp = `# Generated by bosh-agent auto lo iface lo inet loopback auto ethdhcp iface ethdhcp inet dhcp auto ethstatic iface ethstatic inet static address 1.2.3.4 network 1.2.3.0 netmask 255.255.255.0 broadcast 1.2.3.255 gateway 3.4.5.6 dns-nameservers 8.8.8.8 9.9.9.9` }) It("writes interfaces in /etc/network/interfaces in alphabetic order", func() { anotherDHCPNetwork := boshsettings.Network{ Type: "dynamic", Default: []string{"dns"}, DNS: []string{"8.8.8.8", "9.9.9.9"}, Mac: "fake-another-mac-address", } stubInterfaces(map[string]boshsettings.Network{ "ethstatic": staticNetwork, "ethdhcp1": dhcpNetwork, "ethdhcp0": anotherDHCPNetwork, }) err := netManager.SetupNetworking(boshsettings.Networks{ "dhcp-network-1": dhcpNetwork, "dhcp-network-2": anotherDHCPNetwork, "static-network": staticNetwork, }, nil) Expect(err).ToNot(HaveOccurred()) networkConfig := fs.GetFileTestStat("/etc/network/interfaces") Expect(networkConfig).ToNot(BeNil()) expectedNetworkConfigurationForStaticAndDhcp = `# Generated by bosh-agent auto lo iface lo inet loopback auto ethdhcp0 iface ethdhcp0 inet dhcp auto ethdhcp1 iface ethdhcp1 inet dhcp auto ethstatic iface ethstatic inet static address 1.2.3.4 network 1.2.3.0 netmask 255.255.255.0 broadcast 1.2.3.255 gateway 3.4.5.6 dns-nameservers 8.8.8.8 9.9.9.9` Expect(networkConfig.StringContents()).To(Equal(expectedNetworkConfigurationForStaticAndDhcp)) }) It("writes /etc/network/interfaces without dns-namservers if there are no dns servers", func() { staticNetworkWithoutDNS := boshsettings.Network{ Type: "manual", IP: "1.2.3.4", Netmask: "255.255.255.0", Gateway: "3.4.5.6", Mac: "fake-static-mac-address", } stubInterfaces(map[string]boshsettings.Network{ "ethstatic": staticNetworkWithoutDNS, }) err := netManager.SetupNetworking(boshsettings.Networks{"static-network": staticNetworkWithoutDNS}, nil) Expect(err).ToNot(HaveOccurred()) networkConfig := fs.GetFileTestStat("/etc/network/interfaces") Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(`# Generated by bosh-agent auto lo iface lo inet loopback auto ethstatic iface ethstatic inet static address 1.2.3.4 network 1.2.3.0 netmask 255.255.255.0 broadcast 1.2.3.255 gateway 3.4.5.6 `)) }) It("returns errors from glob /sys/class/net/", func() { fs.GlobErr = errors.New("fs-glob-error") err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fs-glob-error")) }) It("returns errors from writing the network configuration", func() { stubInterfaces(map[string]boshsettings.Network{ "dhcp": dhcpNetwork, "static": staticNetwork, }) fs.WriteFileError = errors.New("fs-write-file-error") err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fs-write-file-error")) }) It("returns errors when it can't creating network interface configurations", func() { stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) staticNetwork.Netmask = "not an ip" //will cause InterfaceConfigurationCreator to fail err := netManager.SetupNetworking(boshsettings.Networks{"static-network": staticNetwork}, nil) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Creating interface configurations")) }) It("writes a dhcp configuration if there are dhcp networks", func() { stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).ToNot(HaveOccurred()) dhcpConfig := fs.GetFileTestStat("/etc/dhcp/dhclient.conf") Expect(dhcpConfig).ToNot(BeNil()) Expect(dhcpConfig.StringContents()).To(Equal(`# Generated by bosh-agent option rfc3442-classless-static-routes code 121 = array of unsigned integer 8; send host-name "<hostname>"; request subnet-mask, broadcast-address, time-offset, routers, domain-name, domain-name-servers, domain-search, host-name, netbios-name-servers, netbios-scope, interface-mtu, rfc3442-classless-static-routes, ntp-servers; prepend domain-name-servers 8.8.8.8, 9.9.9.9; `)) }) It("writes a dhcp configuration without prepended dns servers if there are no dns servers specified", func() { dhcpNetworkWithoutDNS := boshsettings.Network{ Type: "dynamic", Mac: "fake-dhcp-mac-address", } stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, }) err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetworkWithoutDNS}, nil) Expect(err).ToNot(HaveOccurred()) dhcpConfig := fs.GetFileTestStat("/etc/dhcp/dhclient.conf") Expect(dhcpConfig).ToNot(BeNil()) Expect(dhcpConfig.StringContents()).To(Equal(`# Generated by bosh-agent option rfc3442-classless-static-routes code 121 = array of unsigned integer 8; send host-name "<hostname>"; request subnet-mask, broadcast-address, time-offset, routers, domain-name, domain-name-servers, domain-search, host-name, netbios-name-servers, netbios-scope, interface-mtu, rfc3442-classless-static-routes, ntp-servers; `)) }) It("returns an error if it can't write a dhcp configuration", func() { stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) fs.WriteFileErrors["/etc/dhcp/dhclient.conf"] = errors.New("dhclient.conf-write-error") err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("dhclient.conf-write-error")) }) It("doesn't write a dhcp configuration if there are no dhcp networks", func() { stubInterfaces(map[string]boshsettings.Network{ "ethstatic": staticNetwork, }) err := netManager.SetupNetworking(boshsettings.Networks{"static-network": staticNetwork}, nil) Expect(err).ToNot(HaveOccurred()) dhcpConfig := fs.GetFileTestStat("/etc/dhcp/dhclient.conf") Expect(dhcpConfig).To(BeNil()) }) It("restarts the networks if /etc/network/interfaces changes", func() { initialDhcpConfig := `# Generated by bosh-agent option rfc3442-classless-static-routes code 121 = array of unsigned integer 8; send host-name "<hostname>"; request subnet-mask, broadcast-address, time-offset, routers, domain-name, domain-name-servers, domain-search, host-name, netbios-name-servers, netbios-scope, interface-mtu, rfc3442-classless-static-routes, ntp-servers; prepend domain-name-servers 8.8.8.8, 9.9.9.9; ` stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) fs.WriteFileString("/etc/dhcp/dhclient.conf", initialDhcpConfig) err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).ToNot(HaveOccurred()) Expect(len(cmdRunner.RunCommands)).To(Equal(5)) Expect(cmdRunner.RunCommands[0]).To(Equal([]string{"pkill", "dhclient"})) Expect(cmdRunner.RunCommands[1:3]).To(ContainElement([]string{"resolvconf", "-d", "ethdhcp.dhclient"})) Expect(cmdRunner.RunCommands[1:3]).To(ContainElement([]string{"resolvconf", "-d", "ethstatic.dhclient"})) Expect(cmdRunner.RunCommands[3]).To(Equal([]string{"ifdown", "-a", "--no-loopback"})) Expect(cmdRunner.RunCommands[4]).To(Equal([]string{"ifup", "-a", "--no-loopback"})) }) It("doesn't restart the networks if /etc/network/interfaces and /etc/dhcp/dhclient.conf don't change", func() { initialDhcpConfig := `# Generated by bosh-agent option rfc3442-classless-static-routes code 121 = array of unsigned integer 8; send host-name "<hostname>"; request subnet-mask, broadcast-address, time-offset, routers, domain-name, domain-name-servers, domain-search, host-name, netbios-name-servers, netbios-scope, interface-mtu, rfc3442-classless-static-routes, ntp-servers; prepend domain-name-servers 8.8.8.8, 9.9.9.9; ` stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) fs.WriteFileString("/etc/network/interfaces", expectedNetworkConfigurationForStaticAndDhcp) fs.WriteFileString("/etc/dhcp/dhclient.conf", initialDhcpConfig) err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).ToNot(HaveOccurred()) networkConfig := fs.GetFileTestStat("/etc/network/interfaces") Expect(networkConfig.StringContents()).To(Equal(expectedNetworkConfigurationForStaticAndDhcp)) dhcpConfig := fs.GetFileTestStat("/etc/dhcp/dhclient.conf") Expect(dhcpConfig.StringContents()).To(Equal(initialDhcpConfig)) Expect(len(cmdRunner.RunCommands)).To(Equal(0)) }) It("restarts the networks if /etc/dhcp/dhclient.conf changes", func() { stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) fs.WriteFileString("/etc/network/interfaces", expectedNetworkConfigurationForStaticAndDhcp) err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).ToNot(HaveOccurred()) networkConfig := fs.GetFileTestStat("/etc/network/interfaces") Expect(networkConfig.StringContents()).To(Equal(expectedNetworkConfigurationForStaticAndDhcp)) Expect(len(cmdRunner.RunCommands)).To(Equal(5)) Expect(cmdRunner.RunCommands[0]).To(Equal([]string{"pkill", "dhclient"})) Expect(cmdRunner.RunCommands[1:3]).To(ContainElement([]string{"resolvconf", "-d", "ethdhcp.dhclient"})) Expect(cmdRunner.RunCommands[1:3]).To(ContainElement([]string{"resolvconf", "-d", "ethstatic.dhclient"})) Expect(cmdRunner.RunCommands[3]).To(Equal([]string{"ifdown", "-a", "--no-loopback"})) Expect(cmdRunner.RunCommands[4]).To(Equal([]string{"ifup", "-a", "--no-loopback"})) }) It("broadcasts MAC addresses for all interfaces", func() { stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) errCh := make(chan error) err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, errCh) Expect(err).ToNot(HaveOccurred()) broadcastErr := <-errCh // wait for all arpings Expect(broadcastErr).ToNot(HaveOccurred()) Expect(addressBroadcaster.BroadcastMACAddressesAddresses).To(Equal([]boship.InterfaceAddress{ boship.NewSimpleInterfaceAddress("ethstatic", "1.2.3.4"), boship.NewResolvingInterfaceAddress("ethdhcp", ipResolver), })) }) It("skips vip networks", func() { stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) vipNetwork := boshsettings.Network{ Type: "vip", Default: []string{"dns"}, DNS: []string{"8.8.8.8", "9.9.9.9"}, Mac: "fake-vip-mac-address", IP: "9.8.7.6", } err := netManager.SetupNetworking(boshsettings.Networks{ "dhcp-network": dhcpNetwork, "static-network": staticNetwork, "vip-network": vipNetwork, }, nil) Expect(err).ToNot(HaveOccurred()) networkConfig := fs.GetFileTestStat("/etc/network/interfaces") Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(expectedNetworkConfigurationForStaticAndDhcp)) }) Context("when no MAC address is provided in the settings", func() { It("configures network for single device", func() { staticNetworkWithoutMAC := boshsettings.Network{ Type: "manual", IP: "2.2.2.2", Netmask: "255.255.255.0", Gateway: "3.4.5.6", } stubInterfaces( map[string]boshsettings.Network{ "ethstatic": staticNetwork, }, ) err := netManager.SetupNetworking(boshsettings.Networks{ "static-network": staticNetworkWithoutMAC, }, nil) Expect(err).ToNot(HaveOccurred()) networkConfig := fs.GetFileTestStat("/etc/network/interfaces") Expect(networkConfig).ToNot(BeNil()) expectedNetworkConfiguration := `# Generated by bosh-agent auto lo iface lo inet loopback auto ethstatic iface ethstatic inet static address 2.2.2.2 network 2.2.2.0 netmask 255.255.255.0 broadcast 2.2.2.255 gateway 3.4.5.6 ` Expect(networkConfig.StringContents()).To(Equal(expectedNetworkConfiguration)) }) It("configures network for a single physical device, when a virtual device is also present", func() { staticNetworkWithoutMAC := boshsettings.Network{ Type: "manual", IP: "2.2.2.2", Netmask: "255.255.255.0", Gateway: "3.4.5.6", } stubInterfacesWithVirtual( map[string]boshsettings.Network{ "ethstatic": staticNetwork, }, []string{"virtual"}, ) err := netManager.SetupNetworking(boshsettings.Networks{ "static-network": staticNetworkWithoutMAC, }, nil) Expect(err).ToNot(HaveOccurred()) networkConfig := fs.GetFileTestStat("/etc/network/interfaces") Expect(networkConfig).ToNot(BeNil()) expectedNetworkConfiguration := `# Generated by bosh-agent auto lo iface lo inet loopback auto ethstatic iface ethstatic inet static address 2.2.2.2 network 2.2.2.0 netmask 255.255.255.0 broadcast 2.2.2.255 gateway 3.4.5.6 ` Expect(networkConfig.StringContents()).To(Equal(expectedNetworkConfiguration)) }) }) }) Describe("GetConfiguredNetworkInterfaces", func() { Context("when there are network devices", func() { BeforeEach(func() { interfacePaths := []string{} interfacePaths = append(interfacePaths, writeNetworkDevice("fake-eth0", "aa:bb", true)) interfacePaths = append(interfacePaths, writeNetworkDevice("fake-eth1", "cc:dd", true)) interfacePaths = append(interfacePaths, writeNetworkDevice("fake-eth2", "ee:ff", true)) fs.SetGlob("/sys/class/net/*", interfacePaths) }) It("returns networks that are defined in /etc/network/interfaces", func() { cmdRunner.AddCmdResult("ifup --no-act fake-eth0", fakesys.FakeCmdResult{ Stdout: "", Stderr: "ifup: interface fake-eth0 already configured", ExitStatus: 0, }) cmdRunner.AddCmdResult("ifup --no-act fake-eth1", fakesys.FakeCmdResult{ Stdout: "", Stderr: "Ignoring unknown interface fake-eth1=fake-eth1.", ExitStatus: 0, }) cmdRunner.AddCmdResult("ifup --no-act fake-eth2", fakesys.FakeCmdResult{ Stdout: "", Stderr: "ifup: interface fake-eth2 already configured", ExitStatus: 0, }) interfaces, err := netManager.GetConfiguredNetworkInterfaces() Expect(err).ToNot(HaveOccurred()) Expect(interfaces).To(ConsistOf("fake-eth0", "fake-eth2")) }) }) Context("when there are no network devices", func() { It("returns empty list", func() { interfaces, err := netManager.GetConfiguredNetworkInterfaces() Expect(err).ToNot(HaveOccurred()) Expect(interfaces).To(Equal([]string{})) }) }) }) }
func init() { Describe("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 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)) }) }) }) }) }
return } var _ = Describe("SignalableLogger", func() { var ( signalChannel chan os.Signal ) BeforeEach(func() { signalChannel = make(chan os.Signal, 1) }) Describe("Toggling forced debug", func() { Describe("when the log level is error", func() { It("outputs at debug level", func() { stdout, stderr := captureOutputs(func() { logger, doneChannel := bilog.NewSignalableLogger(boshlog.NewLogger(boshlog.LevelError), signalChannel) signalChannel <- syscall.SIGHUP <-doneChannel logger.Debug("TOGGLED_DEBUG", "some debug log") logger.Info("TOGGLED_INFO", "some info log") logger.Warn("TOGGLED_WARN", "some warn log") logger.Error("TOGGLED_ERROR", "some error log") }) Expect(stdout).To(ContainSubstring("TOGGLED_DEBUG")) Expect(stdout).To(ContainSubstring("TOGGLED_INFO")) Expect(stderr).To(ContainSubstring("TOGGLED_WARN")) Expect(stderr).To(ContainSubstring("TOGGLED_ERROR")) })
func init() { Describe("Testing with Ginkgo", func() { It("home dir", func() { osFs, _ := createOsFs() homeDir, err := osFs.HomeDir("root") Expect(err).ToNot(HaveOccurred()) Expect(homeDir).To(ContainSubstring("/root")) }) It("expand path", func() { osFs, _ := createOsFs() expandedPath, err := osFs.ExpandPath("~/fake-dir/fake-file.txt") Expect(err).ToNot(HaveOccurred()) currentUser, err := osuser.Current() Expect(err).ToNot(HaveOccurred()) Expect(expandedPath).To(Equal(currentUser.HomeDir + "/fake-dir/fake-file.txt")) expandedPath, err = osFs.ExpandPath("/fake-dir//fake-file.txt") Expect(err).ToNot(HaveOccurred()) Expect(expandedPath).To(Equal("/fake-dir/fake-file.txt")) expandedPath, err = osFs.ExpandPath("./fake-file.txt") Expect(err).ToNot(HaveOccurred()) currentDir, err := os.Getwd() Expect(err).ToNot(HaveOccurred()) Expect(expandedPath).To(Equal(currentDir + "/fake-file.txt")) }) It("mkdir all", func() { osFs, _ := createOsFs() tmpPath := os.TempDir() testPath := filepath.Join(tmpPath, "MkdirAllTestDir", "bar", "baz") defer os.RemoveAll(filepath.Join(tmpPath, "MkdirAllTestDir")) _, err := os.Stat(testPath) Expect(err).To(HaveOccurred()) Expect(os.IsNotExist(err)).To(BeTrue()) fileMode := os.FileMode(0700) err = osFs.MkdirAll(testPath, fileMode) Expect(err).ToNot(HaveOccurred()) stat, err := os.Stat(testPath) Expect(err).ToNot(HaveOccurred()) Expect(stat.IsDir()).To(BeTrue()) Expect(stat.Mode().Perm()).To(Equal(fileMode)) err = osFs.MkdirAll(testPath, fileMode) Expect(err).ToNot(HaveOccurred()) }) It("chown", func() { osFs, _ := createOsFs() testPath := filepath.Join(os.TempDir(), "ChownTestDir") err := os.Mkdir(testPath, os.FileMode(0700)) Expect(err).ToNot(HaveOccurred()) defer os.RemoveAll(testPath) err = osFs.Chown(testPath, "root") Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("not permitted")) }) It("chmod", func() { osFs, _ := createOsFs() testPath := filepath.Join(os.TempDir(), "ChmodTestDir") _, err := os.Create(testPath) Expect(err).ToNot(HaveOccurred()) defer os.Remove(testPath) os.Chmod(testPath, os.FileMode(0666)) err = osFs.Chmod(testPath, os.FileMode(0644)) Expect(err).ToNot(HaveOccurred()) fileStat, err := os.Stat(testPath) Expect(err).ToNot(HaveOccurred()) Expect(fileStat.Mode()).To(Equal(os.FileMode(0644))) }) It("opens file", func() { osFs, _ := createOsFs() testPath := filepath.Join(os.TempDir(), "OpenFileTestFile") file, err := osFs.OpenFile(testPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.FileMode(0644)) defer os.Remove(testPath) Expect(err).ToNot(HaveOccurred()) file.Write([]byte("testing new file")) file.Close() createdFile, err := os.Open(testPath) Expect(err).ToNot(HaveOccurred()) defer createdFile.Close() Expect(readFile(createdFile)).To(Equal("testing new file")) }) Context("the file already exists and is not write only", func() { It("writes to file", func() { osFs, _ := createOsFs() testPath := filepath.Join(os.TempDir(), "subDir", "ConvergeFileContentsTestFile") _, err := os.Stat(testPath) Expect(err).To(HaveOccurred()) written, err := osFs.ConvergeFileContents(testPath, []byte("initial write")) Expect(err).ToNot(HaveOccurred()) Expect(written).To(BeTrue()) defer os.Remove(testPath) file, err := os.Open(testPath) Expect(err).ToNot(HaveOccurred()) defer file.Close() Expect(readFile(file)).To(Equal("initial write")) written, err = osFs.ConvergeFileContents(testPath, []byte("second write")) Expect(err).ToNot(HaveOccurred()) Expect(written).To(BeTrue()) file.Close() file, err = os.Open(testPath) Expect(err).ToNot(HaveOccurred()) Expect(readFile(file)).To(Equal("second write")) file.Close() file, err = os.Open(testPath) written, err = osFs.ConvergeFileContents(testPath, []byte("second write")) Expect(err).ToNot(HaveOccurred()) Expect(written).To(BeFalse()) Expect(readFile(file)).To(Equal("second write")) }) }) Context("the file already exists and is write only", func() { It("writes to file", func() { osFs, _ := createOsFs() testPath := filepath.Join(os.TempDir(), "subDir", "ConvergeFileContentsTestFile") _, err := os.OpenFile(testPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(0200)) Expect(err).ToNot(HaveOccurred()) defer os.Remove(testPath) err = osFs.WriteFile(testPath, []byte("test")) Expect(err).ToNot(HaveOccurred()) }) }) Context("the file parent fir does not exist", func() { BeforeEach(func() { err := os.RemoveAll(filepath.Join(os.TempDir(), "subDirNew")) Expect(err).ToNot(HaveOccurred()) }) AfterEach(func() { err := os.RemoveAll(filepath.Join(os.TempDir(), "subDirNew")) Expect(err).ToNot(HaveOccurred()) }) It("writes to file", func() { osFs, _ := createOsFs() testPath := filepath.Join(os.TempDir(), "subDirNew", "ConvergeFileContentsTestFile") err := osFs.WriteFile(testPath, []byte("test")) Expect(err).ToNot(HaveOccurred()) }) }) It("read file", func() { osFs, _ := createOsFs() testPath := filepath.Join(os.TempDir(), "ReadFileTestFile") osFs.WriteFileString(testPath, "some contents") defer os.Remove(testPath) content, err := osFs.ReadFile(testPath) Expect(err).ToNot(HaveOccurred()) Expect("some contents").To(Equal(string(content))) }) It("file exists", func() { osFs, _ := createOsFs() testPath := filepath.Join(os.TempDir(), "FileExistsTestFile") Expect(osFs.FileExists(testPath)).To(BeFalse()) osFs.WriteFileString(testPath, "initial write") defer os.Remove(testPath) Expect(osFs.FileExists(testPath)).To(BeTrue()) }) It("rename", func() { osFs, _ := createOsFs() tempDir := os.TempDir() oldPath := filepath.Join(tempDir, "old") oldFilePath := filepath.Join(oldPath, "test.txt") newPath := filepath.Join(tempDir, "new") os.Mkdir(oldPath, os.ModePerm) _, err := os.Create(oldFilePath) Expect(err).ToNot(HaveOccurred()) err = osFs.Rename(oldPath, newPath) Expect(err).ToNot(HaveOccurred()) Expect(osFs.FileExists(newPath)).To(BeTrue()) newFilePath := filepath.Join(newPath, "test.txt") Expect(osFs.FileExists(newFilePath)).To(BeTrue()) }) It("symlink", func() { osFs, _ := createOsFs() filePath := filepath.Join(os.TempDir(), "SymlinkTestFile") containingDir := filepath.Join(os.TempDir(), "SubDir") os.Remove(containingDir) symlinkPath := filepath.Join(containingDir, "SymlinkTestSymlink") osFs.WriteFileString(filePath, "some content") defer os.Remove(filePath) osFs.Symlink(filePath, symlinkPath) defer os.Remove(containingDir) symlinkStats, err := os.Lstat(symlinkPath) Expect(err).ToNot(HaveOccurred()) Expect(os.ModeSymlink).To(Equal(os.ModeSymlink & symlinkStats.Mode())) symlinkFile, err := os.Open(symlinkPath) Expect(err).ToNot(HaveOccurred()) Expect("some content").To(Equal(readFile(symlinkFile))) }) It("symlink when link already exists and links to the intended path", func() { osFs, _ := createOsFs() filePath := filepath.Join(os.TempDir(), "SymlinkTestIdempotent1File") symlinkPath := filepath.Join(os.TempDir(), "SymlinkTestIdempotent1Symlink") osFs.WriteFileString(filePath, "some content") defer os.Remove(filePath) osFs.Symlink(filePath, symlinkPath) defer os.Remove(symlinkPath) firstSymlinkStats, err := os.Lstat(symlinkPath) Expect(err).ToNot(HaveOccurred()) err = osFs.Symlink(filePath, symlinkPath) Expect(err).ToNot(HaveOccurred()) secondSymlinkStats, err := os.Lstat(symlinkPath) Expect(err).ToNot(HaveOccurred()) Expect(firstSymlinkStats.ModTime()).To(Equal(secondSymlinkStats.ModTime())) }) It("symlink when link already exists and does not link to the intended path", func() { osFs, _ := createOsFs() filePath := filepath.Join(os.TempDir(), "SymlinkTestIdempotent1File") otherFilePath := filepath.Join(os.TempDir(), "SymlinkTestIdempotent1OtherFile") symlinkPath := filepath.Join(os.TempDir(), "SymlinkTestIdempotent1Symlink") osFs.WriteFileString(filePath, "some content") defer os.Remove(filePath) osFs.WriteFileString(otherFilePath, "other content") defer os.Remove(otherFilePath) err := osFs.Symlink(otherFilePath, symlinkPath) Expect(err).ToNot(HaveOccurred()) err = osFs.Symlink(filePath, symlinkPath) Expect(err).ToNot(HaveOccurred()) defer os.Remove(symlinkPath) symlinkStats, err := os.Lstat(symlinkPath) Expect(err).ToNot(HaveOccurred()) Expect(os.ModeSymlink).To(Equal(os.ModeSymlink & symlinkStats.Mode())) symlinkFile, err := os.Open(symlinkPath) Expect(err).ToNot(HaveOccurred()) Expect("some content").To(Equal(readFile(symlinkFile))) }) It("symlink when a file exists at intended path", func() { osFs, _ := createOsFs() filePath := filepath.Join(os.TempDir(), "SymlinkTestIdempotent1File") symlinkPath := filepath.Join(os.TempDir(), "SymlinkTestIdempotent1Symlink") osFs.WriteFileString(filePath, "some content") defer os.Remove(filePath) osFs.WriteFileString(symlinkPath, "some other content") defer os.Remove(symlinkPath) err := osFs.Symlink(filePath, symlinkPath) Expect(err).ToNot(HaveOccurred()) defer os.Remove(symlinkPath) symlinkStats, err := os.Lstat(symlinkPath) Expect(err).ToNot(HaveOccurred()) Expect(os.ModeSymlink).To(Equal(os.ModeSymlink & symlinkStats.Mode())) symlinkFile, err := os.Open(symlinkPath) Expect(err).ToNot(HaveOccurred()) Expect("some content").To(Equal(readFile(symlinkFile))) }) It("read link", func() { osFs, _ := createOsFs() filePath := filepath.Join(os.TempDir(), "SymlinkTestFile") containingDir := filepath.Join(os.TempDir(), "SubDir") os.Remove(containingDir) symlinkPath := filepath.Join(containingDir, "SymlinkTestSymlink") osFs.WriteFileString(filePath, "some content") defer os.Remove(filePath) err := osFs.Symlink(filePath, symlinkPath) Expect(err).ToNot(HaveOccurred()) defer os.Remove(containingDir) actualFilePath, err := osFs.ReadLink(symlinkPath) Expect(err).ToNot(HaveOccurred()) // on Mac OS /var -> private/var absPath, err := filepath.EvalSymlinks(filePath) Expect(err).ToNot(HaveOccurred()) Expect(actualFilePath).To(Equal(absPath)) }) It("temp file", func() { osFs, _ := createOsFs() file1, err := osFs.TempFile("fake-prefix") Expect(err).ToNot(HaveOccurred()) assert.NotEmpty(GinkgoT(), file1) defer os.Remove(file1.Name()) file2, err := osFs.TempFile("fake-prefix") Expect(err).ToNot(HaveOccurred()) assert.NotEmpty(GinkgoT(), file2) defer os.Remove(file2.Name()) assert.NotEqual(GinkgoT(), file1.Name(), file2.Name()) }) It("temp dir", func() { osFs, _ := createOsFs() path1, err := osFs.TempDir("fake-prefix") Expect(err).ToNot(HaveOccurred()) assert.NotEmpty(GinkgoT(), path1) defer os.Remove(path1) path2, err := osFs.TempDir("fake-prefix") Expect(err).ToNot(HaveOccurred()) assert.NotEmpty(GinkgoT(), path2) defer os.Remove(path2) assert.NotEqual(GinkgoT(), path1, path2) }) Describe("Temporary directories and files", func() { var ( osFs FileSystem testTempDir string ) BeforeEach(func() { osFs, _ = createOsFs() var err error testTempDir, err = ioutil.TempDir("", "os_filesystem_test") Expect(err).ToNot(HaveOccurred()) }) AfterEach(func() { os.Remove(testTempDir) }) Context("a temp root is set", func() { BeforeEach(func() { osFs.ChangeTempRoot(testTempDir) }) It("creates temp files under that root", func() { file, err := osFs.TempFile("some-file-prefix") Expect(err).ToNot(HaveOccurred()) Expect(file.Name()).To(HavePrefix(filepath.Join(testTempDir, "some-file-prefix"))) }) It("creates temp directories under that root", func() { dirName, err := osFs.TempDir("some-dir-prefix") Expect(err).ToNot(HaveOccurred()) Expect(dirName).To(HavePrefix(filepath.Join(testTempDir, "some-dir-prefix"))) }) }) Context("no temp root is set and was initialized as a strict temp root", func() { BeforeEach(func() { osFs = NewOsFileSystemWithStrictTempRoot(boshlog.NewLogger(boshlog.LevelNone)) }) It("should eror", func() { _, err := osFs.TempFile("some-prefix") Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("ChangeTempRoot")) _, err = osFs.TempDir("some-prefix") Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("ChangeTempRoot")) }) }) }) Describe("CopyFile", func() { It("copies file", func() { osFs, _ := createOsFs() srcPath := "test_assets/test_copy_dir_entries/foo.txt" dstFile, err := osFs.TempFile("CopyFileTestFile") Expect(err).ToNot(HaveOccurred()) defer os.Remove(dstFile.Name()) err = osFs.CopyFile(srcPath, dstFile.Name()) fooContent, err := osFs.ReadFileString(dstFile.Name()) Expect(err).ToNot(HaveOccurred()) Expect(fooContent).To(Equal("foo\n")) }) It("does not leak file descriptors", func() { osFs, _ := createOsFs() srcFile, err := osFs.TempFile("srcPath") Expect(err).ToNot(HaveOccurred()) err = srcFile.Close() Expect(err).ToNot(HaveOccurred()) dstFile, err := osFs.TempFile("dstPath") Expect(err).ToNot(HaveOccurred()) err = dstFile.Close() Expect(err).ToNot(HaveOccurred()) err = osFs.CopyFile(srcFile.Name(), dstFile.Name()) Expect(err).ToNot(HaveOccurred()) runner := NewExecCmdRunner(boshlog.NewLogger(boshlog.LevelNone)) stdout, _, _, err := runner.RunCommand("lsof") Expect(err).ToNot(HaveOccurred()) for _, line := range strings.Split(stdout, "\n") { if strings.Contains(line, srcFile.Name()) { Fail(fmt.Sprintf("CopyFile did not close: srcFile: %s", srcFile.Name())) } if strings.Contains(line, dstFile.Name()) { Fail(fmt.Sprintf("CopyFile did not close: dstFile: %s", dstFile.Name())) } } os.Remove(srcFile.Name()) os.Remove(dstFile.Name()) }) }) Describe("CopyDir", func() { var fixtureFiles = []string{ "foo.txt", "bar/bar.txt", "bar/baz/.gitkeep", } It("recursively copies directory contents", func() { osFs, _ := createOsFs() srcPath := "test_assets/test_copy_dir_entries" dstPath, err := osFs.TempDir("CopyDirTestDir") Expect(err).ToNot(HaveOccurred()) defer osFs.RemoveAll(dstPath) err = osFs.CopyDir(srcPath, dstPath) Expect(err).ToNot(HaveOccurred()) for _, fixtureFile := range fixtureFiles { srcContents, err := osFs.ReadFile(filepath.Join(srcPath, fixtureFile)) Expect(err).ToNot(HaveOccurred()) dstContents, err := osFs.ReadFile(filepath.Join(dstPath, fixtureFile)) Expect(err).ToNot(HaveOccurred()) Expect(srcContents).To(Equal(dstContents), "Copied file does not match source file: '%s", fixtureFile) } }) It("does not leak file descriptors", func() { osFs, _ := createOsFs() srcPath := "test_assets/test_copy_dir_entries" dstPath, err := osFs.TempDir("CopyDirTestDir") Expect(err).ToNot(HaveOccurred()) defer osFs.RemoveAll(dstPath) err = osFs.CopyDir(srcPath, dstPath) Expect(err).ToNot(HaveOccurred()) runner := NewExecCmdRunner(boshlog.NewLogger(boshlog.LevelNone)) stdout, _, _, err := runner.RunCommand("lsof") Expect(err).ToNot(HaveOccurred()) // lsof uses absolute paths srcPath, err = filepath.Abs(srcPath) Expect(err).ToNot(HaveOccurred()) for _, line := range strings.Split(stdout, "\n") { for _, fixtureFile := range fixtureFiles { srcFilePath := filepath.Join(srcPath, fixtureFile) if strings.Contains(line, srcFilePath) { Fail(fmt.Sprintf("CopyDir did not close source file: %s", srcFilePath)) } srcFileDirPath := filepath.Dir(srcFilePath) if strings.Contains(line, srcFileDirPath) { Fail(fmt.Sprintf("CopyDir did not close source dir: %s", srcFileDirPath)) } dstFilePath := filepath.Join(dstPath, fixtureFile) if strings.Contains(line, dstFilePath) { Fail(fmt.Sprintf("CopyDir did not close destination file: %s", dstFilePath)) } dstFileDirPath := filepath.Dir(dstFilePath) if strings.Contains(line, dstFileDirPath) { Fail(fmt.Sprintf("CopyDir did not close destination dir: %s", dstFileDirPath)) } } } }) }) It("remove all", func() { osFs, _ := createOsFs() dstFile, err := osFs.TempFile("CopyFileTestFile") Expect(err).ToNot(HaveOccurred()) defer os.Remove(dstFile.Name()) err = osFs.RemoveAll(dstFile.Name()) Expect(err).ToNot(HaveOccurred()) _, err = os.Stat(dstFile.Name()) Expect(os.IsNotExist(err)).To(BeTrue()) }) }) }
"github.com/cloudfoundry/bosh-init/internal/github.com/cloudfoundry/bosh-utils/logger" "github.com/cloudfoundry/bosh-init/internal/github.com/cloudfoundry/bosh-utils/system" fakesys "github.com/cloudfoundry/bosh-init/internal/github.com/cloudfoundry/bosh-utils/system/fakes" ) const cert1 string = `-----BEGIN CERTIFICATE----- MIIEJDCCAwygAwIBAgIJAO+CqgiJnCgpMA0GCSqGSIb3DQEBBQUAMGkxCzAJBgNV BAYTAkNBMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX qokoSBXzJCJTt2P681gyqBDr/hUYzqpoXUsOTRisScbEbaSv8hTiTeFJUMyNQAqn DtmvI8bXKxU= -----END CERTIFICATE-----` var _ = Describe("Certificate Management", func() { var log logger.Logger BeforeEach(func() { log = logger.NewLogger(logger.LevelNone) }) Describe("CertificateSplitting", func() { It("splits 2 back-to-back certificates", func() { certs := fmt.Sprintf("%s\n%s\n", cert1, cert1) result := cert.SplitCerts(certs) Expect(result[0]).To(Equal(cert1)) Expect(result[1]).To(Equal(cert1)) Expect(len(result)).To(Equal(2)) }) It("splits 2 back-to-back certificates without trailing newline", func() { certs := fmt.Sprintf("%s\n%s", cert1, cert1)
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).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")) }) }) }) }
func describeConfigDriveMetadataService() { var ( metadataService MetadataService resolver *fakeinf.FakeDNSResolver platform *fakeplatform.FakePlatform logger boshlog.Logger ) updateMetadata := func(metadataContents MetadataContentsType) { metadataJSON, err := json.Marshal(metadataContents) Expect(err).ToNot(HaveOccurred()) platform.SetGetFilesContentsFromDisk("/fake-disk-path-1/fake-metadata-path", metadataJSON, nil) Expect(metadataService.IsAvailable()).To(BeTrue()) } updateUserdata := func(userdataContents string) { platform.SetGetFilesContentsFromDisk("/fake-disk-path-1/fake-userdata-path", []byte(userdataContents), nil) Expect(metadataService.IsAvailable()).To(BeTrue()) } BeforeEach(func() { resolver = &fakeinf.FakeDNSResolver{} platform = fakeplatform.NewFakePlatform() logger = boshlog.NewLogger(boshlog.LevelNone) diskPaths := []string{ "/fake-disk-path-1", "/fake-disk-path-2", } metadataService = NewConfigDriveMetadataService( resolver, platform, diskPaths, "fake-metadata-path", "fake-userdata-path", logger, ) userdataContents := fmt.Sprintf(`{"server":{"name":"fake-server-name"},"registry":{"endpoint":"fake-registry-endpoint"}}`) platform.SetGetFilesContentsFromDisk("/fake-disk-path-1/fake-userdata-path", []byte(userdataContents), nil) metadata := MetadataContentsType{ PublicKeys: map[string]PublicKeyType{ "0": PublicKeyType{ "openssh-key": "fake-openssh-key", }, }, InstanceID: "fake-instance-id", } updateMetadata(metadata) }) Describe("GetNetworks", func() { It("returns the network settings", func() { userdataContents := ` { "networks": { "network_1": {"type": "manual", "ip": "1.2.3.4", "netmask": "2.3.4.5", "gateway": "3.4.5.6", "default": ["dns"], "dns": ["8.8.8.8"], "mac": "fake-mac-address-1"}, "network_2": {"type": "dynamic", "default": ["dns"], "dns": ["8.8.8.8"], "mac": "fake-mac-address-2"} } }` updateUserdata(userdataContents) networks, err := metadataService.GetNetworks() Expect(err).ToNot(HaveOccurred()) Expect(networks).To(Equal(boshsettings.Networks{ "network_1": boshsettings.Network{ Type: "manual", IP: "1.2.3.4", Netmask: "2.3.4.5", Gateway: "3.4.5.6", Default: []string{"dns"}, DNS: []string{"8.8.8.8"}, Mac: "fake-mac-address-1", }, "network_2": boshsettings.Network{ Type: "dynamic", Default: []string{"dns"}, DNS: []string{"8.8.8.8"}, Mac: "fake-mac-address-2", }, })) }) It("returns a nil Networks if the settings are missing (from an old CPI version)", func() { userdataContents := `{}` updateUserdata(userdataContents) networks, err := metadataService.GetNetworks() Expect(err).ToNot(HaveOccurred()) Expect(networks).To(BeNil()) }) }) Describe("IsAvailable", func() { It("return true when it can load successfully", func() { Expect(metadataService.IsAvailable()).To(BeTrue()) }) It("returns an error if it fails to read meta-data.json from disk", func() { platform.SetGetFilesContentsFromDisk("/fake-disk-path-1/fake-metadata-path", []byte{}, errors.New("fake-read-disk-error")) Expect(metadataService.IsAvailable()).To(BeFalse()) }) It("tries to load meta-data.json from potential disk locations", func() { platform.SetGetFilesContentsFromDisk("/fake-disk-path-1/fake-metadata-path", []byte{}, errors.New("fake-read-disk-error")) Expect(metadataService.IsAvailable()).To(BeFalse()) Expect(platform.GetFileContentsFromDiskDiskPaths).To(ContainElement("/fake-disk-path-1")) Expect(platform.GetFileContentsFromDiskDiskPaths).To(ContainElement("/fake-disk-path-2")) }) It("returns an error if it fails to parse meta-data.json contents", func() { platform.SetGetFilesContentsFromDisk("/fake-disk-path-1/fake-metadata-path", []byte("broken"), nil) Expect(metadataService.IsAvailable()).To(BeFalse()) }) It("returns an error if it fails to read user_data from disk", func() { platform.SetGetFilesContentsFromDisk("/fake-disk-path-1/fake-userdata-path", []byte{}, errors.New("fake-read-disk-error")) Expect(metadataService.IsAvailable()).To(BeFalse()) }) It("returns an error if it fails to parse user_data contents", func() { platform.SetGetFilesContentsFromDisk("/fake-disk-path-1/fake-userdata-path", []byte("broken"), nil) Expect(metadataService.IsAvailable()).To(BeFalse()) }) Context("when disk paths are not given", func() { It("returns false", func() { metadataService = NewConfigDriveMetadataService( resolver, platform, []string{}, "fake-metadata-path", "fake-userdata-path", logger, ) Expect(metadataService.IsAvailable()).To(BeFalse()) }) }) }) Describe("GetPublicKey", func() { It("returns public key", func() { value, err := metadataService.GetPublicKey() Expect(err).ToNot(HaveOccurred()) Expect(value).To(Equal("fake-openssh-key")) }) It("returns an error if it fails to get ssh key", func() { updateMetadata(MetadataContentsType{}) value, err := metadataService.GetPublicKey() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Failed to load openssh-key from config drive metadata service")) Expect(value).To(Equal("")) }) }) Describe("GetInstanceID", func() { It("returns instance id", func() { value, err := metadataService.GetInstanceID() Expect(err).ToNot(HaveOccurred()) Expect(value).To(Equal("fake-instance-id")) }) It("returns an error if it fails to get instance id", func() { updateMetadata(MetadataContentsType{}) value, err := metadataService.GetInstanceID() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Failed to load instance-id from config drive metadata service")) Expect(value).To(Equal("")) }) }) Describe("GetServerName", func() { It("returns server name", func() { value, err := metadataService.GetServerName() Expect(err).ToNot(HaveOccurred()) Expect(value).To(Equal("fake-server-name")) }) It("returns an error if it fails to get server name", func() { updateUserdata("{}") value, err := metadataService.GetServerName() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Failed to load server name from config drive metadata service")) Expect(value).To(Equal("")) }) }) Describe("GetRegistryEndpoint", func() { It("returns an error if it fails to get registry endpoint", func() { updateUserdata("{}") value, err := metadataService.GetRegistryEndpoint() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Failed to load registry endpoint from config drive metadata service")) Expect(value).To(Equal("")) }) Context("when user_data does not contain a dns server", func() { It("returns registry endpoint", func() { value, err := metadataService.GetRegistryEndpoint() Expect(err).ToNot(HaveOccurred()) Expect(value).To(Equal("fake-registry-endpoint")) }) }) Context("when user_data contains a dns server", func() { BeforeEach(func() { userdataContents := fmt.Sprintf( `{"server":{"name":"%s"},"registry":{"endpoint":"%s"},"dns":{"nameserver":["%s"]}}`, "fake-server-name", "http://fake-registry.com", "fake-dns-server-ip", ) updateUserdata(userdataContents) }) Context("when registry endpoint is successfully resolved", func() { BeforeEach(func() { resolver.RegisterRecord(fakeinf.FakeDNSRecord{ DNSServers: []string{"fake-dns-server-ip"}, Host: "http://fake-registry.com", IP: "http://fake-registry-ip", }) }) It("returns the successfully resolved registry endpoint", func() { endpoint, err := metadataService.GetRegistryEndpoint() Expect(err).ToNot(HaveOccurred()) Expect(endpoint).To(Equal("http://fake-registry-ip")) }) }) Context("when registry endpoint is not successfully resolved", func() { BeforeEach(func() { resolver.LookupHostErr = errors.New("fake-lookup-host-err") }) It("returns error because it failed to resolve registry endpoint", func() { endpoint, err := metadataService.GetRegistryEndpoint() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fake-lookup-host-err")) Expect(endpoint).To(BeEmpty()) }) }) }) }) }
func describeCentosNetManager() { var ( fs *fakesys.FakeFileSystem cmdRunner *fakesys.FakeCmdRunner ipResolver *fakeip.FakeResolver addressBroadcaster *fakearp.FakeAddressBroadcaster netManager Manager interfaceConfigurationCreator InterfaceConfigurationCreator ) BeforeEach(func() { fs = fakesys.NewFakeFileSystem() cmdRunner = fakesys.NewFakeCmdRunner() ipResolver = &fakeip.FakeResolver{} logger := boshlog.NewLogger(boshlog.LevelNone) interfaceConfigurationCreator = NewInterfaceConfigurationCreator(logger) addressBroadcaster = &fakearp.FakeAddressBroadcaster{} netManager = NewCentosNetManager( fs, cmdRunner, ipResolver, interfaceConfigurationCreator, addressBroadcaster, logger, ) }) writeNetworkDevice := func(iface string, macAddress string, isPhysical bool) string { interfacePath := fmt.Sprintf("/sys/class/net/%s", iface) fs.WriteFile(interfacePath, []byte{}) if isPhysical { fs.WriteFile(fmt.Sprintf("/sys/class/net/%s/device", iface), []byte{}) } fs.WriteFileString(fmt.Sprintf("/sys/class/net/%s/address", iface), fmt.Sprintf("%s\n", macAddress)) return interfacePath } Describe("SetupNetworking", func() { var ( dhcpNetwork boshsettings.Network staticNetwork boshsettings.Network expectedNetworkConfigurationForStatic string expectedNetworkConfigurationForDHCP string expectedDhclientConfiguration string ) BeforeEach(func() { dhcpNetwork = boshsettings.Network{ Type: "dynamic", Default: []string{"dns"}, DNS: []string{"8.8.8.8", "9.9.9.9"}, Mac: "fake-dhcp-mac-address", } staticNetwork = boshsettings.Network{ Type: "manual", IP: "1.2.3.4", Netmask: "255.255.255.0", Gateway: "3.4.5.6", Mac: "fake-static-mac-address", } expectedNetworkConfigurationForStatic = `DEVICE=ethstatic BOOTPROTO=static IPADDR=1.2.3.4 NETMASK=255.255.255.0 BROADCAST=1.2.3.255 GATEWAY=3.4.5.6 ONBOOT=yes PEERDNS=no DNS1=8.8.8.8 DNS2=9.9.9.9 ` expectedNetworkConfigurationForDHCP = `DEVICE=ethdhcp BOOTPROTO=dhcp ONBOOT=yes PEERDNS=yes ` expectedDhclientConfiguration = `# Generated by bosh-agent option rfc3442-classless-static-routes code 121 = array of unsigned integer 8; send host-name "<hostname>"; request subnet-mask, broadcast-address, time-offset, routers, domain-name, domain-name-servers, domain-search, host-name, netbios-name-servers, netbios-scope, interface-mtu, rfc3442-classless-static-routes, ntp-servers; prepend domain-name-servers 8.8.8.8, 9.9.9.9; ` }) stubInterfacesWithVirtual := func(physicalInterfaces map[string]boshsettings.Network, virtualInterfaces []string) { interfacePaths := []string{} for iface, networkSettings := range physicalInterfaces { interfacePaths = append(interfacePaths, writeNetworkDevice(iface, networkSettings.Mac, true)) } for _, iface := range virtualInterfaces { interfacePaths = append(interfacePaths, writeNetworkDevice(iface, "virtual", false)) } fs.SetGlob("/sys/class/net/*", interfacePaths) } stubInterfaces := func(physicalInterfaces map[string]boshsettings.Network) { stubInterfacesWithVirtual(physicalInterfaces, nil) } It("writes a network script for static and dynamic interfaces", func() { stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).ToNot(HaveOccurred()) staticConfig := fs.GetFileTestStat("/etc/sysconfig/network-scripts/ifcfg-ethstatic") Expect(staticConfig).ToNot(BeNil()) Expect(staticConfig.StringContents()).To(Equal(expectedNetworkConfigurationForStatic)) dhcpConfig := fs.GetFileTestStat("/etc/sysconfig/network-scripts/ifcfg-ethdhcp") Expect(dhcpConfig).ToNot(BeNil()) Expect(dhcpConfig.StringContents()).To(Equal(expectedNetworkConfigurationForDHCP)) }) It("returns errors from glob /sys/class/net/", func() { fs.GlobErr = errors.New("fs-glob-error") err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fs-glob-error")) }) It("returns errors from writing the network configuration", func() { stubInterfaces(map[string]boshsettings.Network{ "dhcp": dhcpNetwork, "static": staticNetwork, }) fs.WriteFileError = errors.New("fs-write-file-error") err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("fs-write-file-error")) }) It("returns errors when it can't create network interface configurations", func() { stubInterfaces(map[string]boshsettings.Network{ "ethstatic": staticNetwork, }) staticNetwork.Netmask = "not an ip" //will cause InterfaceConfigurationCreator to fail err := netManager.SetupNetworking(boshsettings.Networks{"static-network": staticNetwork}, nil) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("Creating interface configurations")) }) It("wrtites a dhcp configuration if there are dhcp networks", func() { stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).ToNot(HaveOccurred()) dhcpConfig := fs.GetFileTestStat("/etc/dhcp/dhclient.conf") Expect(dhcpConfig).ToNot(BeNil()) Expect(dhcpConfig.StringContents()).To(Equal(expectedDhclientConfiguration)) dhcpConfigSymlink := fs.GetFileTestStat("/etc/dhcp/dhclient-ethdhcp.conf") Expect(dhcpConfigSymlink).ToNot(BeNil()) Expect(dhcpConfigSymlink.SymlinkTarget).To(Equal("/etc/dhcp/dhclient.conf")) }) It("writes a dhcp configuration without prepended dns servers if there are no dns servers specified", func() { dhcpNetworkWithoutDNS := boshsettings.Network{ Type: "dynamic", Mac: "fake-dhcp-mac-address", } stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, }) err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetworkWithoutDNS}, nil) Expect(err).ToNot(HaveOccurred()) dhcpConfig := fs.GetFileTestStat("/etc/dhcp/dhclient.conf") Expect(dhcpConfig).ToNot(BeNil()) Expect(dhcpConfig.StringContents()).To(Equal(`# Generated by bosh-agent option rfc3442-classless-static-routes code 121 = array of unsigned integer 8; send host-name "<hostname>"; request subnet-mask, broadcast-address, time-offset, routers, domain-name, domain-name-servers, domain-search, host-name, netbios-name-servers, netbios-scope, interface-mtu, rfc3442-classless-static-routes, ntp-servers; `)) dhcpConfigSymlink := fs.GetFileTestStat("/etc/dhcp/dhclient-ethdhcp.conf") Expect(dhcpConfigSymlink).ToNot(BeNil()) Expect(dhcpConfigSymlink.SymlinkTarget).To(Equal("/etc/dhcp/dhclient.conf")) }) It("returns an error if it can't write a dhcp configuration", func() { stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) fs.WriteFileErrors["/etc/dhcp/dhclient.conf"] = errors.New("dhclient.conf-write-error") err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("dhclient.conf-write-error")) }) It("returns an error if it can't symlink a dhcp configuration", func() { stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) fs.SymlinkError = errors.New("dhclient-ethdhcp.conf-symlink-error") err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("dhclient-ethdhcp.conf-symlink-error")) }) It("doesn't write a dhcp configuration if there are no dhcp networks", func() { stubInterfaces(map[string]boshsettings.Network{ "ethstatic": staticNetwork, }) err := netManager.SetupNetworking(boshsettings.Networks{"static-network": staticNetwork}, nil) Expect(err).ToNot(HaveOccurred()) dhcpConfig := fs.GetFileTestStat("/etc/dhcp/dhclient-ethdhcp.conf") Expect(dhcpConfig).To(BeNil()) }) It("restarts the networks if any ifconfig file changes", func() { changingStaticNetwork := boshsettings.Network{ Type: "manual", IP: "1.2.3.5", Netmask: "255.255.255.0", Gateway: "3.4.5.6", Mac: "ethstatict-that-changes", } stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic-that-changes": changingStaticNetwork, "ethstatic": staticNetwork, }) fs.WriteFileString("/etc/sysconfig/network-scripts/ifcfg-ethstatic", expectedNetworkConfigurationForStatic) fs.WriteFileString("/etc/dhcp/dhclient.conf", expectedDhclientConfiguration) err := netManager.SetupNetworking(boshsettings.Networks{ "dhcp-network": dhcpNetwork, "changing-static-network": changingStaticNetwork, "static-network": staticNetwork, }, nil) Expect(err).ToNot(HaveOccurred()) Expect(len(cmdRunner.RunCommands)).To(Equal(1)) Expect(cmdRunner.RunCommands[0]).To(Equal([]string{"service", "network", "restart"})) }) It("doesn't restart the networks if ifcfg and /etc/dhcp/dhclient.conf don't change", func() { stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) fs.WriteFileString("/etc/sysconfig/network-scripts/ifcfg-ethstatic", expectedNetworkConfigurationForStatic) fs.WriteFileString("/etc/sysconfig/network-scripts/ifcfg-ethdhcp", expectedNetworkConfigurationForDHCP) fs.WriteFileString("/etc/dhcp/dhclient.conf", expectedDhclientConfiguration) err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).ToNot(HaveOccurred()) networkConfig := fs.GetFileTestStat("/etc/sysconfig/network-scripts/ifcfg-ethstatic") Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(expectedNetworkConfigurationForStatic)) dhcpConfig := fs.GetFileTestStat("/etc/dhcp/dhclient.conf") Expect(dhcpConfig.StringContents()).To(Equal(expectedDhclientConfiguration)) Expect(len(cmdRunner.RunCommands)).To(Equal(0)) }) It("restarts the networks if /etc/dhcp/dhclient.conf changes", func() { stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) fs.WriteFileString("/etc/sysconfig/network-scripts/ifcfg-ethstatic", expectedNetworkConfigurationForStatic) err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, nil) Expect(err).ToNot(HaveOccurred()) networkConfig := fs.GetFileTestStat("/etc/sysconfig/network-scripts/ifcfg-ethstatic") Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(expectedNetworkConfigurationForStatic)) Expect(len(cmdRunner.RunCommands)).To(Equal(1)) Expect(cmdRunner.RunCommands[0]).To(Equal([]string{"service", "network", "restart"})) }) It("broadcasts MAC addresses for all interfaces", func() { stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) errCh := make(chan error) err := netManager.SetupNetworking(boshsettings.Networks{"dhcp-network": dhcpNetwork, "static-network": staticNetwork}, errCh) Expect(err).ToNot(HaveOccurred()) broadcastErr := <-errCh // wait for all arpings Expect(broadcastErr).ToNot(HaveOccurred()) Expect(addressBroadcaster.BroadcastMACAddressesAddresses).To(Equal([]boship.InterfaceAddress{ boship.NewSimpleInterfaceAddress("ethstatic", "1.2.3.4"), boship.NewResolvingInterfaceAddress("ethdhcp", ipResolver), })) }) It("skips vip networks", func() { stubInterfaces(map[string]boshsettings.Network{ "ethdhcp": dhcpNetwork, "ethstatic": staticNetwork, }) vipNetwork := boshsettings.Network{ Type: "vip", Default: []string{"dns"}, DNS: []string{"4.4.4.4", "5.5.5.5"}, Mac: "fake-vip-mac-address", IP: "9.8.7.6", } err := netManager.SetupNetworking(boshsettings.Networks{ "dhcp-network": dhcpNetwork, "static-network": staticNetwork, "vip-network": vipNetwork, }, nil) Expect(err).ToNot(HaveOccurred()) networkConfig := fs.GetFileTestStat("/etc/sysconfig/network-scripts/ifcfg-ethstatic") Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(expectedNetworkConfigurationForStatic)) }) It("doesn't use vip networks dns", func() { stubInterfaces(map[string]boshsettings.Network{ "ethstatic": staticNetwork, }) vipNetwork := boshsettings.Network{ Type: "vip", Default: []string{"dns"}, DNS: []string{"4.4.4.4", "5.5.5.5"}, Mac: "fake-vip-mac-address", IP: "9.8.7.6", } err := netManager.SetupNetworking(boshsettings.Networks{ "vip-network": vipNetwork, "static-network": staticNetwork, }, nil) Expect(err).ToNot(HaveOccurred()) networkConfig := fs.GetFileTestStat("/etc/sysconfig/network-scripts/ifcfg-ethstatic") Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).ToNot(ContainSubstring("4.4.4.4")) Expect(networkConfig.StringContents()).ToNot(ContainSubstring("5.5.5.5")) }) Context("when no MAC address is provided in the settings", func() { var staticNetworkWithoutMAC boshsettings.Network BeforeEach(func() { staticNetworkWithoutMAC = boshsettings.Network{ Type: "manual", IP: "1.2.3.4", Netmask: "255.255.255.0", Gateway: "3.4.5.6", DNS: []string{"8.8.8.8", "9.9.9.9"}, Default: []string{"dns"}, } }) It("configures network for single device", func() { stubInterfaces( map[string]boshsettings.Network{ "ethstatic": staticNetwork, }, ) err := netManager.SetupNetworking(boshsettings.Networks{ "static-network": staticNetworkWithoutMAC, }, nil) Expect(err).ToNot(HaveOccurred()) networkConfig := fs.GetFileTestStat("/etc/sysconfig/network-scripts/ifcfg-ethstatic") Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(expectedNetworkConfigurationForStatic)) }) It("configures network for single device, when a virtual device is also present", func() { stubInterfacesWithVirtual( map[string]boshsettings.Network{ "ethstatic": staticNetwork, }, []string{"virtual"}, ) err := netManager.SetupNetworking(boshsettings.Networks{ "static-network": staticNetworkWithoutMAC, }, nil) Expect(err).ToNot(HaveOccurred()) physicalNetworkConfig := fs.GetFileTestStat("/etc/sysconfig/network-scripts/ifcfg-ethstatic") Expect(physicalNetworkConfig).ToNot(BeNil()) Expect(physicalNetworkConfig.StringContents()).To(Equal(expectedNetworkConfigurationForStatic)) virtualNetworkConfig := fs.GetFileTestStat("/etc/sysconfig/network-scripts/ifcfg-virtual") Expect(virtualNetworkConfig).To(BeNil()) }) }) }) Describe("GetConfiguredNetworkInterfaces", func() { Context("when there are network devices", func() { BeforeEach(func() { interfacePaths := []string{} interfacePaths = append(interfacePaths, writeNetworkDevice("fake-eth0", "aa:bb", true)) interfacePaths = append(interfacePaths, writeNetworkDevice("fake-eth1", "cc:dd", true)) interfacePaths = append(interfacePaths, writeNetworkDevice("fake-eth2", "ee:ff", true)) fs.SetGlob("/sys/class/net/*", interfacePaths) }) writeIfcgfFile := func(iface string) { fs.WriteFileString(fmt.Sprintf("/etc/sysconfig/network-scripts/ifcfg-%s", iface), "fake-config") } It("returns networks that have ifcfg config present", func() { writeIfcgfFile("fake-eth0") writeIfcgfFile("fake-eth2") interfaces, err := netManager.GetConfiguredNetworkInterfaces() Expect(err).ToNot(HaveOccurred()) Expect(interfaces).To(ConsistOf("fake-eth0", "fake-eth2")) }) }) Context("when there are no network devices", func() { It("returns empty list", func() { interfaces, err := netManager.GetConfiguredNetworkInterfaces() Expect(err).ToNot(HaveOccurred()) Expect(interfaces).To(Equal([]string{})) }) }) }) }
func 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 describeBuilder() { var mockCtrl *gomock.Controller BeforeEach(func() { mockCtrl = gomock.NewController(GinkgoT()) }) AfterEach(func() { mockCtrl.Finish() }) var ( logger boshlog.Logger mockReleaseJobResolver *mock_deployment_release.MockJobResolver mockDependencyCompiler *mock_state_job.MockDependencyCompiler mockJobListRenderer *mock_template.MockJobListRenderer mockCompressor *mock_template.MockRenderedJobListCompressor mockBlobstore *mock_blobstore.MockBlobstore stateBuilder Builder ) BeforeEach(func() { logger = boshlog.NewLogger(boshlog.LevelNone) mockReleaseJobResolver = mock_deployment_release.NewMockJobResolver(mockCtrl) mockDependencyCompiler = mock_state_job.NewMockDependencyCompiler(mockCtrl) mockJobListRenderer = mock_template.NewMockJobListRenderer(mockCtrl) mockCompressor = mock_template.NewMockRenderedJobListCompressor(mockCtrl) mockBlobstore = mock_blobstore.NewMockBlobstore(mockCtrl) }) Describe("Build", func() { var ( mockRenderedJobList *mock_template.MockRenderedJobList mockRenderedJobListArchive *mock_template.MockRenderedJobListArchive jobName string instanceID int deploymentManifest bideplmanifest.Manifest fakeStage *fakebiui.FakeStage releasePackageLibyaml *birelpkg.Package releasePackageRuby *birelpkg.Package releasePackageCPI *birelpkg.Package expectCompile *gomock.Call ) BeforeEach(func() { mockRenderedJobList = mock_template.NewMockRenderedJobList(mockCtrl) mockRenderedJobListArchive = mock_template.NewMockRenderedJobListArchive(mockCtrl) jobName = "fake-deployment-job-name" instanceID = 0 deploymentManifest = bideplmanifest.Manifest{ Name: "fake-deployment-name", Jobs: []bideplmanifest.Job{ { Name: "fake-deployment-job-name", Networks: []bideplmanifest.JobNetwork{ { Name: "fake-network-name", }, }, Templates: []bideplmanifest.ReleaseJobRef{ { Name: "fake-release-job-name", Release: "fake-release-name", }, }, Properties: biproperty.Map{ "fake-job-property": "fake-job-property-value", }, }, }, Networks: []bideplmanifest.Network{ { Name: "fake-network-name", Type: "fake-network-type", CloudProperties: biproperty.Map{ "fake-network-cloud-property": "fake-network-cloud-property-value", }, }, }, Properties: biproperty.Map{ "fake-job-property": "fake-global-property-value", //overridden by job property value }, } fakeStage = fakebiui.NewFakeStage() stateBuilder = NewBuilder( mockReleaseJobResolver, mockDependencyCompiler, mockJobListRenderer, mockCompressor, mockBlobstore, logger, ) releasePackageLibyaml = &birelpkg.Package{ Name: "libyaml", Fingerprint: "fake-package-source-fingerprint-libyaml", SHA1: "fake-package-source-sha1-libyaml", Dependencies: []*birelpkg.Package{}, ArchivePath: "fake-package-archive-path-libyaml", // only required by compiler... } releasePackageRuby = &birelpkg.Package{ Name: "ruby", Fingerprint: "fake-package-source-fingerprint-ruby", SHA1: "fake-package-source-sha1-ruby", Dependencies: []*birelpkg.Package{releasePackageLibyaml}, ArchivePath: "fake-package-archive-path-ruby", // only required by compiler... } releasePackageCPI = &birelpkg.Package{ Name: "cpi", Fingerprint: "fake-package-source-fingerprint-cpi", SHA1: "fake-package-source-sha1-cpi", Dependencies: []*birelpkg.Package{releasePackageRuby}, ArchivePath: "fake-package-archive-path-cpi", // only required by compiler... } }) JustBeforeEach(func() { releaseJob := bireljob.Job{ Name: "fake-release-job-name", Fingerprint: "fake-release-job-source-fingerprint", Packages: []*birelpkg.Package{releasePackageCPI, releasePackageRuby}, } mockReleaseJobResolver.EXPECT().Resolve("fake-release-job-name", "fake-release-name").Return(releaseJob, nil) releaseJobs := []bireljob.Job{releaseJob} compiledPackageRefs := []bistatejob.CompiledPackageRef{ { Name: "libyaml", Version: "fake-package-source-fingerprint-libyaml", BlobstoreID: "fake-package-compiled-archive-blob-id-libyaml", SHA1: "fake-package-compiled-archive-sha1-libyaml", }, { Name: "ruby", Version: "fake-package-source-fingerprint-ruby", BlobstoreID: "fake-package-compiled-archive-blob-id-ruby", SHA1: "fake-package-compiled-archive-sha1-ruby", }, { Name: "cpi", Version: "fake-package-source-fingerprint-cpi", BlobstoreID: "fake-package-compiled-archive-blob-id-cpi", SHA1: "fake-package-compiled-archive-sha1-cpi", }, } expectCompile = mockDependencyCompiler.EXPECT().Compile(releaseJobs, fakeStage).Return(compiledPackageRefs, nil).AnyTimes() jobProperties := biproperty.Map{ "fake-job-property": "fake-job-property-value", } globalProperties := biproperty.Map{ "fake-job-property": "fake-global-property-value", } mockJobListRenderer.EXPECT().Render(releaseJobs, jobProperties, globalProperties, "fake-deployment-name").Return(mockRenderedJobList, nil) mockRenderedJobList.EXPECT().DeleteSilently() mockCompressor.EXPECT().Compress(mockRenderedJobList).Return(mockRenderedJobListArchive, nil) mockRenderedJobListArchive.EXPECT().DeleteSilently() mockRenderedJobListArchive.EXPECT().Path().Return("fake-rendered-job-list-archive-path") mockRenderedJobListArchive.EXPECT().SHA1().Return("fake-rendered-job-list-archive-sha1") mockRenderedJobListArchive.EXPECT().Fingerprint().Return("fake-rendered-job-list-fingerprint") mockBlobstore.EXPECT().Add("fake-rendered-job-list-archive-path").Return("fake-rendered-job-list-archive-blob-id", nil) }) It("compiles the dependencies of the jobs", func() { expectCompile.Times(1) _, err := stateBuilder.Build(jobName, instanceID, deploymentManifest, fakeStage) Expect(err).ToNot(HaveOccurred()) }) It("builds a new instance state with zero-to-many networks", func() { state, err := stateBuilder.Build(jobName, instanceID, deploymentManifest, fakeStage) Expect(err).ToNot(HaveOccurred()) Expect(state.NetworkInterfaces()).To(ContainElement(NetworkRef{ Name: "fake-network-name", Interface: biproperty.Map{ "type": "fake-network-type", "default": []bideplmanifest.NetworkDefault{"dns", "gateway"}, "cloud_properties": biproperty.Map{ "fake-network-cloud-property": "fake-network-cloud-property-value", }, }, })) Expect(state.NetworkInterfaces()).To(HaveLen(1)) }) It("builds a new instance state with zero-to-many rendered jobs from one or more releases", func() { state, err := stateBuilder.Build(jobName, instanceID, deploymentManifest, fakeStage) Expect(err).ToNot(HaveOccurred()) Expect(state.RenderedJobs()).To(ContainElement(JobRef{ Name: "fake-release-job-name", Version: "fake-release-job-source-fingerprint", })) // multiple jobs are rendered in a single archive Expect(state.RenderedJobListArchive()).To(Equal(BlobRef{ BlobstoreID: "fake-rendered-job-list-archive-blob-id", SHA1: "fake-rendered-job-list-archive-sha1", })) Expect(state.RenderedJobs()).To(HaveLen(1)) }) It("prints ui stages for compiling packages and rendering job templates", func() { _, err := stateBuilder.Build(jobName, instanceID, deploymentManifest, fakeStage) Expect(err).ToNot(HaveOccurred()) Expect(fakeStage.PerformCalls).To(Equal([]*fakebiui.PerformCall{ // compile stages not produced by mockDependencyCompiler {Name: "Rendering job templates"}, })) }) It("builds a new instance state with the compiled packages required by the release jobs", func() { state, err := stateBuilder.Build(jobName, instanceID, deploymentManifest, fakeStage) Expect(err).ToNot(HaveOccurred()) Expect(state.CompiledPackages()).To(ContainElement(PackageRef{ Name: "cpi", Version: "fake-package-source-fingerprint-cpi", Archive: BlobRef{ SHA1: "fake-package-compiled-archive-sha1-cpi", BlobstoreID: "fake-package-compiled-archive-blob-id-cpi", }, })) Expect(state.CompiledPackages()).To(ContainElement(PackageRef{ Name: "ruby", Version: "fake-package-source-fingerprint-ruby", Archive: BlobRef{ SHA1: "fake-package-compiled-archive-sha1-ruby", BlobstoreID: "fake-package-compiled-archive-blob-id-ruby", }, })) }) It("builds a new instance state that includes transitively dependent compiled packages", func() { state, err := stateBuilder.Build(jobName, instanceID, deploymentManifest, fakeStage) Expect(err).ToNot(HaveOccurred()) Expect(state.CompiledPackages()).To(ContainElement(PackageRef{ Name: "cpi", Version: "fake-package-source-fingerprint-cpi", Archive: BlobRef{ SHA1: "fake-package-compiled-archive-sha1-cpi", BlobstoreID: "fake-package-compiled-archive-blob-id-cpi", }, })) Expect(state.CompiledPackages()).To(ContainElement(PackageRef{ Name: "ruby", Version: "fake-package-source-fingerprint-ruby", Archive: BlobRef{ SHA1: "fake-package-compiled-archive-sha1-ruby", BlobstoreID: "fake-package-compiled-archive-blob-id-ruby", }, })) Expect(state.CompiledPackages()).To(ContainElement(PackageRef{ Name: "libyaml", Version: "fake-package-source-fingerprint-libyaml", Archive: BlobRef{ SHA1: "fake-package-compiled-archive-sha1-libyaml", BlobstoreID: "fake-package-compiled-archive-blob-id-libyaml", }, })) Expect(state.CompiledPackages()).To(HaveLen(3)) }) Context("when multiple packages have the same dependency", func() { BeforeEach(func() { releasePackageRuby.Dependencies = append(releasePackageRuby.Dependencies, releasePackageLibyaml) }) It("does not recompile dependant packages", func() { state, err := stateBuilder.Build(jobName, instanceID, deploymentManifest, fakeStage) Expect(err).ToNot(HaveOccurred()) Expect(state.CompiledPackages()).To(ContainElement(PackageRef{ Name: "cpi", Version: "fake-package-source-fingerprint-cpi", Archive: BlobRef{ SHA1: "fake-package-compiled-archive-sha1-cpi", BlobstoreID: "fake-package-compiled-archive-blob-id-cpi", }, })) Expect(state.CompiledPackages()).To(ContainElement(PackageRef{ Name: "ruby", Version: "fake-package-source-fingerprint-ruby", Archive: BlobRef{ SHA1: "fake-package-compiled-archive-sha1-ruby", BlobstoreID: "fake-package-compiled-archive-blob-id-ruby", }, })) Expect(state.CompiledPackages()).To(ContainElement(PackageRef{ Name: "libyaml", Version: "fake-package-source-fingerprint-libyaml", Archive: BlobRef{ SHA1: "fake-package-compiled-archive-sha1-libyaml", BlobstoreID: "fake-package-compiled-archive-blob-id-libyaml", }, })) Expect(state.CompiledPackages()).To(HaveLen(3)) }) }) It("builds an instance state that can be converted to an ApplySpec", func() { state, err := stateBuilder.Build(jobName, instanceID, deploymentManifest, fakeStage) Expect(err).ToNot(HaveOccurred()) Expect(state.ToApplySpec()).To(Equal(bias.ApplySpec{ Deployment: "fake-deployment-name", Index: 0, Networks: map[string]biproperty.Map{ "fake-network-name": biproperty.Map{ "type": "fake-network-type", "default": []bideplmanifest.NetworkDefault{"dns", "gateway"}, "cloud_properties": biproperty.Map{ "fake-network-cloud-property": "fake-network-cloud-property-value", }, }, }, Job: bias.Job{ Name: "fake-deployment-job-name", Templates: []bias.Blob{ { Name: "fake-release-job-name", Version: "fake-release-job-source-fingerprint", }, }, }, Packages: map[string]bias.Blob{ "cpi": bias.Blob{ Name: "cpi", Version: "fake-package-source-fingerprint-cpi", SHA1: "fake-package-compiled-archive-sha1-cpi", BlobstoreID: "fake-package-compiled-archive-blob-id-cpi", }, "ruby": bias.Blob{ Name: "ruby", Version: "fake-package-source-fingerprint-ruby", SHA1: "fake-package-compiled-archive-sha1-ruby", BlobstoreID: "fake-package-compiled-archive-blob-id-ruby", }, "libyaml": bias.Blob{ Name: "libyaml", Version: "fake-package-source-fingerprint-libyaml", SHA1: "fake-package-compiled-archive-sha1-libyaml", BlobstoreID: "fake-package-compiled-archive-blob-id-libyaml", }, }, RenderedTemplatesArchive: bias.RenderedTemplatesArchiveSpec{ BlobstoreID: "fake-rendered-job-list-archive-blob-id", SHA1: "fake-rendered-job-list-archive-sha1", }, ConfigurationHash: "fake-rendered-job-list-fingerprint", })) }) }) }
return "", errors.New("fake-get-ip-err") } var _ = Describe("arping", func() { const arpingIterations = 6 var ( fs *fakesys.FakeFileSystem cmdRunner *fakesys.FakeCmdRunner arping AddressBroadcaster ) BeforeEach(func() { fs = fakesys.NewFakeFileSystem() cmdRunner = fakesys.NewFakeCmdRunner() logger := boshlog.NewLogger(boshlog.LevelNone) arping = NewArping(cmdRunner, fs, logger, arpingIterations, 0, 0) }) Describe("BroadcastMACAddresses", func() { BeforeEach(func() { fs.WriteFile("/sys/class/net/eth0", []byte{}) fs.WriteFile("/sys/class/net/eth1", []byte{}) }) It("runs arping commands for each interface", func() { addresses := []boship.InterfaceAddress{ boship.NewSimpleInterfaceAddress("eth0", "192.168.195.6"), boship.NewSimpleInterfaceAddress("eth1", "127.0.0.1"), }
func createOsFs() (fs FileSystem, runner CmdRunner) { logger := boshlog.NewLogger(boshlog.LevelNone) fs = NewOsFileSystem(logger) return }