Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions agent/action/add_dynamic_disk.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package action

import (
"errors"

boshplatform "github.com/cloudfoundry/bosh-agent/v2/platform"
boshsettings "github.com/cloudfoundry/bosh-agent/v2/settings"
bosherr "github.com/cloudfoundry/bosh-utils/errors"
)

type AddDynamicDiskAction struct {
settingsService boshsettings.Service
platform boshplatform.Platform
}

func NewAddDynamicDiskAction(settingsService boshsettings.Service, platform boshplatform.Platform) AddDynamicDiskAction {
return AddDynamicDiskAction{
settingsService: settingsService,
platform: platform,
}
}

func (a AddDynamicDiskAction) Run(diskCID string, diskHint interface{}) (interface{}, error) {
err := a.settingsService.LoadSettings()
if err != nil {
return nil, bosherr.WrapError(err, "Refreshing the settings")
}

currentSettings := a.settingsService.GetSettings()

diskSetting := currentSettings.PersistentDiskSettingsFromHint(diskCID, diskHint)
if err := a.platform.SetupDynamicDisk(diskSetting); err != nil {
return "", bosherr.WrapError(err, "Setting up dynamic disk")
}

return map[string]string{}, nil
}

func (a AddDynamicDiskAction) IsAsynchronous(_ ProtocolVersion) bool {
return true
}

func (a AddDynamicDiskAction) IsPersistent() bool {
return false
}

func (a AddDynamicDiskAction) IsLoggable() bool {
return true
}

func (a AddDynamicDiskAction) Resume() (interface{}, error) {
return nil, errors.New("not supported")
}

func (a AddDynamicDiskAction) Cancel() error {
return errors.New("not supported")
}
49 changes: 49 additions & 0 deletions agent/action/add_dynamic_disk_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package action

import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/pkg/errors"

"github.com/cloudfoundry/bosh-agent/v2/platform/platformfakes"
"github.com/cloudfoundry/bosh-agent/v2/settings"
fakesettings "github.com/cloudfoundry/bosh-agent/v2/settings/fakes"
)

var _ = Describe("AddDynamicDiskAction", func() {
var (
action AddDynamicDiskAction
settingsService *fakesettings.FakeSettingsService
platform *platformfakes.FakePlatform
)

BeforeEach(func() {
settingsService = &fakesettings.FakeSettingsService{}
platform = &platformfakes.FakePlatform{}
action = NewAddDynamicDiskAction(settingsService, platform)
})

It("sets up dynamic disk", func() {
result, err := action.Run("diskCID", "/dev/sdb")

Expect(err).ToNot(HaveOccurred())
Expect(result).To(Equal(map[string]string{}))
Expect(platform.SetupDynamicDiskCallCount()).To(Equal(1))
Expect(platform.SetupDynamicDiskArgsForCall(0)).To(Equal(settings.DiskSettings{
ID: "diskCID",
Path: "/dev/sdb",
VolumeID: "/dev/sdb",
}))
})

Context("when setting up dynamic disk fails", func() {
BeforeEach(func() {
platform.SetupDynamicDiskReturns(errors.New("Could not setup"))
})

It("should raise error", func() {
_, err := action.Run("diskCID", "/dev/sdb")
Expect(err).To(HaveOccurred())
})
})
})
2 changes: 2 additions & 0 deletions agent/action/concrete_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ func NewFactory(
"unmount_disk": NewUnmountDisk(settingsService, platform),
"add_persistent_disk": NewAddPersistentDiskAction(settingsService),
"remove_persistent_disk": NewRemovePersistentDiskAction(settingsService),
"add_dynamic_disk": NewAddDynamicDiskAction(settingsService, platform),
"remove_dynamic_disk": NewRemoveDynamicDiskAction(platform),

// ARP cache management
"delete_arp_entries": NewDeleteARPEntries(platform),
Expand Down
46 changes: 46 additions & 0 deletions agent/action/remove_dynamic_disk.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package action

import (
"errors"

boshplatform "github.com/cloudfoundry/bosh-agent/v2/platform"
bosherr "github.com/cloudfoundry/bosh-utils/errors"
)

type RemoveDynamicDiskAction struct {
platform boshplatform.Platform
}

func NewRemoveDynamicDiskAction(platform boshplatform.Platform) RemoveDynamicDiskAction {
return RemoveDynamicDiskAction{
platform: platform,
}
}

func (a RemoveDynamicDiskAction) Run(diskCID string) (interface{}, error) {
if err := a.platform.CleanupDynamicDisk(diskCID); err != nil {
return "", bosherr.WrapError(err, "Setting up dynamic disk")
}

return map[string]string{}, nil
}

func (a RemoveDynamicDiskAction) IsAsynchronous(_ ProtocolVersion) bool {
return true
}

func (a RemoveDynamicDiskAction) IsPersistent() bool {
return false
}

func (a RemoveDynamicDiskAction) IsLoggable() bool {
return true
}

func (a RemoveDynamicDiskAction) Resume() (interface{}, error) {
return nil, errors.New("not supported")
}

func (a RemoveDynamicDiskAction) Cancel() error {
return errors.New("not supported")
}
41 changes: 41 additions & 0 deletions agent/action/remove_dynamic_disk_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package action

import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/pkg/errors"

"github.com/cloudfoundry/bosh-agent/v2/platform/platformfakes"
)

var _ = Describe("RemoveDynamicDiskAction", func() {
var (
action RemoveDynamicDiskAction
platform *platformfakes.FakePlatform
)

BeforeEach(func() {
platform = &platformfakes.FakePlatform{}
action = NewRemoveDynamicDiskAction(platform)
})

It("cleans up dynamic disk", func() {
result, err := action.Run("diskCID")

Expect(err).ToNot(HaveOccurred())
Expect(result).To(Equal(map[string]string{}))
Expect(platform.CleanupDynamicDiskCallCount()).To(Equal(1))
Expect(platform.CleanupDynamicDiskArgsForCall(0)).To(Equal("diskCID"))
})

Context("when cleaning up dynamic disk fails", func() {
BeforeEach(func() {
platform.CleanupDynamicDiskReturns(errors.New("Could not setup"))
})

It("should raise error", func() {
_, err := action.Run("diskCID")
Expect(err).To(HaveOccurred())
})
})
})
8 changes: 8 additions & 0 deletions platform/dummy_platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,14 @@ func (p dummyPlatform) SetupRawEphemeralDisks(devices []boshsettings.DiskSetting
return
}

func (p dummyPlatform) SetupDynamicDisk(diskSetting boshsettings.DiskSettings) (err error) {
return
}

func (p dummyPlatform) CleanupDynamicDisk(diskCID string) (err error) {
return
}

func (p dummyPlatform) SetupDataDir(_ boshsettings.JobDir, _ boshsettings.RunDir) error {
dataDir := p.dirProvider.DataDir()

Expand Down
37 changes: 37 additions & 0 deletions platform/linux_platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,32 @@ func (p linux) SetupRawEphemeralDisks(devices []boshsettings.DiskSettings) (err
return nil
}

func (p linux) SetupDynamicDisk(diskSetting boshsettings.DiskSettings) error {
devicePath, timedOut, err := p.devicePathResolver.GetRealDevicePath(diskSetting)
if err != nil {
return bosherr.WrapError(err, "Getting dynamic disk real device path")
}
if timedOut {
return bosherr.WrapErrorf(err, "Timed out resolving device path for %s", diskSetting.ID)
}

symlinkDestination := filepath.Join(p.dirProvider.DataDynamicDisksDir(), diskSetting.ID)
if err := p.fs.Symlink(devicePath, symlinkDestination); err != nil {
return bosherr.WrapError(err, "Symlinking dynamic disk real device path")
}

return nil
}

func (p linux) CleanupDynamicDisk(diskCID string) error {
symlinkDestination := filepath.Join(p.dirProvider.DataDynamicDisksDir(), diskCID)
if err := p.fs.RemoveAll(symlinkDestination); err != nil {
return bosherr.WrapError(err, "Removing dynamic disk symlink")
}

return nil
}

func (p linux) SetupDataDir(jobConfig boshsettings.JobDir, runConfig boshsettings.RunDir) error {
dataDir := p.dirProvider.DataDir()

Expand Down Expand Up @@ -805,6 +831,12 @@ func (p linux) SetupDataDir(jobConfig boshsettings.JobDir, runConfig boshsetting
return bosherr.WrapErrorf(err, "Making %s dir", jobsDir)
}

dynamicDisksDir := p.dirProvider.DataDynamicDisksDir()
err = p.fs.MkdirAll(dynamicDisksDir, persistentDiskPermissions)
if err != nil {
return bosherr.WrapErrorf(err, "Making %s dir", dynamicDisksDir)
}

sensitiveDir := p.dirProvider.SensitiveBlobsDir()
err = p.fs.MkdirAll(sensitiveDir, blobsDirPermissions)
if err != nil {
Expand Down Expand Up @@ -835,6 +867,11 @@ func (p linux) SetupDataDir(jobConfig boshsettings.JobDir, runConfig boshsetting
return bosherr.WrapErrorf(err, "chown %s", sensitiveDir)
}

_, _, _, err = p.cmdRunner.RunCommand("chown", "root:vcap", dynamicDisksDir)
if err != nil {
return bosherr.WrapErrorf(err, "chown %s", dynamicDisksDir)
}

packagesDir := p.dirProvider.PkgDir()
err = p.fs.MkdirAll(packagesDir, packagesDirPermissions)
if err != nil {
Expand Down
72 changes: 70 additions & 2 deletions platform/linux_platform_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,63 @@ fake-base-path/data/sys/log/*.log fake-base-path/data/sys/log/.*.log fake-base-p
})
})

Describe("SetupDynamicDisk", func() {
BeforeEach(func() {
devicePathResolver.GetRealDevicePathStub = func(diskSettings boshsettings.DiskSettings) (string, bool, error) {
return diskSettings.Path, false, nil
}
})

It("sets up dynamic disk symlink", func() {
err := platform.SetupDynamicDisk(boshsettings.DiskSettings{ID: "diskID", Path: "/dev/sdb"})
Expect(err).NotTo(HaveOccurred())

sysStats := fs.GetFileTestStat("/fake-dir/data/dynamic_disks/diskID")
Expect(sysStats).ToNot(BeNil())
Expect(sysStats.FileType).To(Equal(fakesys.FakeFileTypeSymlink))
Expect(sysStats.SymlinkTarget).To(Equal("/dev/sdb"))
})

It("returns error if resolving device path fails", func() {
devicePathResolver.GetRealDevicePathStub = func(diskSettings boshsettings.DiskSettings) (string, bool, error) {
return "", false, errors.New("fake-get-real-device-path-err")
}

err := platform.SetupDynamicDisk(boshsettings.DiskSettings{ID: "diskID", Path: "/dev/sdb"})
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("fake-get-real-device-path-err"))
})

It("returns error if resolving device path times out", func() {
devicePathResolver.GetRealDevicePathStub = func(diskSettings boshsettings.DiskSettings) (string, bool, error) {
return "", true, nil
}

err := platform.SetupDynamicDisk(boshsettings.DiskSettings{ID: "diskID", Path: "/dev/sdb"})
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("Timed out resolving device path for diskID"))
})
})

Describe("CleanupDynamicDisk", func() {
BeforeEach(func() {
devicePathResolver.GetRealDevicePathStub = func(diskSettings boshsettings.DiskSettings) (string, bool, error) {
return diskSettings.Path, false, nil
}
})

It("removes dynamic disk symlink", func() {
err := platform.SetupDynamicDisk(boshsettings.DiskSettings{ID: "diskID", Path: "/dev/sdb"})
Expect(err).NotTo(HaveOccurred())

err = platform.CleanupDynamicDisk("diskID")
Expect(err).NotTo(HaveOccurred())

sysStats := fs.GetFileTestStat("/fake-dir/data/dynamic_disks/diskID")
Expect(sysStats).To(BeNil())
})
})

Describe("SetupEphemeralDiskWithPath", func() {
var (
labelPrefix string
Expand Down Expand Up @@ -1882,7 +1939,18 @@ Number Start End Size File system Name Flags
Expect(sysLogStats).ToNot(BeNil())
Expect(sysLogStats.FileType).To(Equal(fakesys.FakeFileTypeDir))
Expect(sysLogStats.FileMode).To(Equal(os.FileMode(0755)))
Expect(cmdRunner.RunCommands[4]).To(Equal([]string{"chown", "root:vcap", "/fake-dir/data/packages"}))
Expect(cmdRunner.RunCommands[5]).To(Equal([]string{"chown", "root:vcap", "/fake-dir/data/packages"}))
})

It("creates dynamic disks directory in data directory", func() {
err := platform.SetupDataDir(boshsettings.JobDir{}, boshsettings.RunDir{})
Expect(err).NotTo(HaveOccurred())

sysLogStats := fs.GetFileTestStat("/fake-dir/data/dynamic_disks")
Expect(sysLogStats).ToNot(BeNil())
Expect(sysLogStats.FileType).To(Equal(fakesys.FakeFileTypeDir))
Expect(sysLogStats.FileMode).To(Equal(os.FileMode(0700)))
Expect(cmdRunner.RunCommands[4]).To(Equal([]string{"chown", "root:vcap", "/fake-dir/data/dynamic_disks"}))
})

Context("when a tmpfs for the sensitive directories is requested", func() {
Expand Down Expand Up @@ -2060,7 +2128,7 @@ Number Start End Size File system Name Flags
Expect(sysRunStats).ToNot(BeNil())
Expect(sysRunStats.FileType).To(Equal(fakesys.FakeFileTypeDir))
Expect(sysRunStats.FileMode).To(Equal(os.FileMode(0750)))
Expect(cmdRunner.RunCommands[5]).To(Equal([]string{"chown", "root:vcap", "/fake-dir/data/sys/run"}))
Expect(cmdRunner.RunCommands[6]).To(Equal([]string{"chown", "root:vcap", "/fake-dir/data/sys/run"}))
})

It("mounts tmpfs to sys/run", func() {
Expand Down
2 changes: 2 additions & 0 deletions platform/platform_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ type Platform interface {
IsPersistentDiskMounted(diskSettings boshsettings.DiskSettings) (result bool, err error)
IsPersistentDiskMountable(diskSettings boshsettings.DiskSettings) (bool, error)
AssociateDisk(name string, settings boshsettings.DiskSettings) error
SetupDynamicDisk(diskSetting boshsettings.DiskSettings) error
CleanupDynamicDisk(diskCID string) error

GetFileContentsFromCDROM(filePath string) (contents []byte, err error)
GetFilesContentsFromDisk(diskPath string, fileNames []string) (contents [][]byte, err error)
Expand Down
Loading
Loading