Skip to content

Commit

Permalink
Allow to receive nats and blobstore updates via update settings action
Browse files Browse the repository at this point in the history
Update settings action now merges NATS and Blobstore settings

- Since NATS and Blobstore settings are not always sent, it ensures the
  old values are preserved if no new ones are sent

Add logic to restart the agent when nats or blobstore update_settings change

- Extract update settings function to help with merging data
- Upgrade counterfeiter

Update settings service to also fetch UpdateSettings

- Attach UpdateSettings to Settings object
- Refactor previous usages of UpdateSettings to use new code

Update usage of Blobstore and MBus to use getter functions

- Those functions return UpdateSettings values if available

[#179396629] Rotation of certificates/credentials stored in the bosh-agent metadata can be updated without recreating the VM

Signed-off-by: Kenneth Lakin <[email protected]>
Signed-off-by: Joseph Palermo <[email protected]>
Signed-off-by: Long Nguyen <[email protected]>
  • Loading branch information
ystros authored and jpalermo committed Oct 7, 2021
1 parent 2813e56 commit 6afc068
Show file tree
Hide file tree
Showing 118 changed files with 27,326 additions and 279 deletions.
3 changes: 2 additions & 1 deletion agent/action/concrete_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
blobdelegator "github.com/cloudfoundry/bosh-agent/agent/httpblobprovider/blobstore_delegator"
boshscript "github.com/cloudfoundry/bosh-agent/agent/script"
boshtask "github.com/cloudfoundry/bosh-agent/agent/task"
"github.com/cloudfoundry/bosh-agent/agent/utils"
boshjobsuper "github.com/cloudfoundry/bosh-agent/jobsupervisor"
boshnotif "github.com/cloudfoundry/bosh-agent/notification"
boshplatform "github.com/cloudfoundry/bosh-agent/platform"
Expand Down Expand Up @@ -55,7 +56,7 @@ func NewFactory(
"ssh": NewSSH(settingsService, platform, dirProvider, logger),
"fetch_logs": NewFetchLogs(compressor, copier, blobstoreDelegator, dirProvider),
"fetch_logs_with_signed_url": NewFetchLogsWithSignedURLAction(compressor, copier, dirProvider, blobstoreDelegator),
"update_settings": NewUpdateSettings(settingsService, platform, certManager, logger),
"update_settings": NewUpdateSettings(settingsService, platform, certManager, logger, utils.NewAgentKiller()),
"shutdown": NewShutdown(platform),

// Job management
Expand Down
16 changes: 0 additions & 16 deletions agent/action/fakes/fake_agent_killer.go

This file was deleted.

27 changes: 15 additions & 12 deletions agent/action/update_settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,25 @@ package action
import (
"errors"

"encoding/json"
"github.com/cloudfoundry/bosh-agent/agent/utils"
"github.com/cloudfoundry/bosh-agent/platform"
"github.com/cloudfoundry/bosh-agent/platform/cert"
boshsettings "github.com/cloudfoundry/bosh-agent/settings"
bosherr "github.com/cloudfoundry/bosh-utils/errors"
"github.com/cloudfoundry/bosh-utils/logger"
"path/filepath"
)

type UpdateSettingsAction struct {
agentKiller utils.Killer
trustedCertManager cert.Manager
logger logger.Logger
settingsService boshsettings.Service
platform platform.Platform
}

func NewUpdateSettings(service boshsettings.Service, platform platform.Platform, trustedCertManager cert.Manager, logger logger.Logger) UpdateSettingsAction {
func NewUpdateSettings(service boshsettings.Service, platform platform.Platform, trustedCertManager cert.Manager, logger logger.Logger, agentKiller utils.Killer) UpdateSettingsAction {
return UpdateSettingsAction{
agentKiller: agentKiller,
trustedCertManager: trustedCertManager,
logger: logger,
settingsService: service,
Expand All @@ -33,14 +34,15 @@ func (a UpdateSettingsAction) IsAsynchronous(_ ProtocolVersion) bool {
}

func (a UpdateSettingsAction) IsPersistent() bool {
return false
return true
}

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

func (a UpdateSettingsAction) Run(newUpdateSettings boshsettings.UpdateSettings) (string, error) {
var restartNeeded bool
err := a.settingsService.LoadSettings()
if err != nil {
return "", err
Expand All @@ -63,22 +65,23 @@ func (a UpdateSettingsAction) Run(newUpdateSettings boshsettings.UpdateSettings)
return "", err
}

updateSettingsJSON, err := json.Marshal(newUpdateSettings)
existingSettings := a.settingsService.GetSettings().UpdateSettings
restartNeeded = existingSettings.MergeSettings(newUpdateSettings)
err = a.settingsService.SaveUpdateSettings(existingSettings)
if err != nil {
return "", bosherr.WrapError(err, "Marshalling updateSettings json")
return "", err
}

updateSettingsPath := filepath.Join(a.platform.GetDirProvider().BoshDir(), "update_settings.json")
err = a.platform.GetFs().WriteFile(updateSettingsPath, updateSettingsJSON)
if err != nil {
return "", bosherr.WrapError(err, "writing update settings json")
if restartNeeded {
a.agentKiller.KillAgent()
panic("This line of code should be unreachable due to killing of agent")
}

return "updated", nil
return "ok", nil
}

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

func (a UpdateSettingsAction) Cancel() error {
Expand Down
54 changes: 39 additions & 15 deletions agent/action/update_settings_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package action_test

import (
"github.com/cloudfoundry/bosh-agent/agent/utils/utilsfakes"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"

"errors"

"path/filepath"

. "github.com/cloudfoundry/bosh-agent/agent/action"
"github.com/cloudfoundry/bosh-agent/platform/cert/certfakes"
"github.com/cloudfoundry/bosh-agent/platform/platformfakes"
Expand All @@ -22,6 +21,7 @@ import (
var _ = Describe("UpdateSettings", func() {
var (
action UpdateSettingsAction
agentKiller utilsfakes.FakeKiller
certManager *certfakes.FakeManager
settingsService *fakesettings.FakeSettingsService
log logger.Logger
Expand All @@ -31,6 +31,7 @@ var _ = Describe("UpdateSettings", func() {
)

BeforeEach(func() {
agentKiller = utilsfakes.FakeKiller{}
log = logger.NewLogger(logger.LevelNone)
certManager = new(certfakes.FakeManager)
settingsService = &fakesettings.FakeSettingsService{}
Expand All @@ -39,35 +40,33 @@ var _ = Describe("UpdateSettings", func() {
fileSystem = fakesys.NewFakeFileSystem()
platform.GetFsReturns(fileSystem)

action = NewUpdateSettings(settingsService, platform, certManager, log)
action = NewUpdateSettings(settingsService, platform, certManager, log, &agentKiller)
newUpdateSettings = boshsettings.UpdateSettings{}
})

AssertActionIsAsynchronous(action)
AssertActionIsNotPersistent(action)
AssertActionIsPersistent(action)
AssertActionIsLoggable(action)

AssertActionIsNotResumable(action)
AssertActionIsResumable(action)
AssertActionIsNotCancelable(action)

Context("on success", func() {
It("returns 'updated'", func() {
It("returns 'ok'", func() {
result, err := action.Run(newUpdateSettings)
Expect(err).ToNot(HaveOccurred())
Expect(result).To(Equal("updated"))
Expect(result).To(Equal("ok"))
})

It("writes the updated settings to a file", func() {
action.Run(newUpdateSettings)
expectedPath := filepath.Join(platform.GetDirProvider().BoshDir(), "update_settings.json")
exists := platform.GetFs().FileExists(expectedPath)
Expect(exists).To(Equal(true))
Expect(settingsService.SaveUpdateSettingsCallCount).To(Equal(1))
})
})

Context("when it cannot write the update settings file", func() {
Context("when it fails to save the UpdateSettings", func() {
BeforeEach(func() {
fileSystem.WriteFileError = errors.New("Fake write error")
settingsService.SaveUpdateSettingsErr = errors.New("Fake write error")
})

It("returns an error", func() {
Expand All @@ -82,7 +81,7 @@ var _ = Describe("UpdateSettings", func() {
log = logger.NewLogger(logger.LevelNone)
certManager = new(certfakes.FakeManager)
certManager.UpdateCertificatesReturns(errors.New("Error"))
action = NewUpdateSettings(settingsService, platform, certManager, log)
action = NewUpdateSettings(settingsService, platform, certManager, log, &agentKiller)
})

It("returns the error", func() {
Expand Down Expand Up @@ -161,15 +160,14 @@ var _ = Describe("UpdateSettings", func() {
DiskCID: "fake-disk-id-2",
}

result, err := action.Run(boshsettings.UpdateSettings{
_, err := action.Run(boshsettings.UpdateSettings{
DiskAssociations: []boshsettings.DiskAssociation{
diskAssociation,
diskAssociation2,
},
})

Expect(err).ToNot(HaveOccurred())
Expect(result).To(Equal("updated"))
Expect(platform.AssociateDiskCallCount()).To(Equal(2))

actualDiskName, actualDiskSettings := platform.AssociateDiskArgsForCall(0)
Expand All @@ -194,5 +192,31 @@ var _ = Describe("UpdateSettings", func() {
Path: "fake-disk-path-2",
}))

updateSettings := settingsService.SaveUpdateSettingsLastArg
Expect(updateSettings.DiskAssociations[0].Name).To(Equal("fake-disk-name"))
})

Context("when updating nats or blobstore settings", func() {
BeforeEach(func() {
newUpdateSettings.Mbus.Cert.CA = "new ca cert"
newUpdateSettings.Blobstores = append(newUpdateSettings.Blobstores, boshsettings.Blobstore{Type: "new blobstore"})
})

It("kills the agent", func() {
Expect(func() {
action.Run(newUpdateSettings)
}).To(Panic())
Expect(agentKiller.KillAgentCallCount()).To(Equal(1))
})

It("persists the new settings", func() {
Expect(func() {
action.Run(newUpdateSettings)
}).To(Panic())

updateSettings := settingsService.SaveUpdateSettingsLastArg
Expect(updateSettings.Mbus.Cert.CA).To(Equal("new ca cert"))
Expect(updateSettings.Blobstores[0].Type).To(Equal("new blobstore"))
})
})
})
16 changes: 1 addition & 15 deletions agent/bootstrap.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package agent

import (
"encoding/json"
"errors"
"fmt"
"path"
Expand Down Expand Up @@ -215,20 +214,7 @@ func (boot bootstrap) Run() (err error) {
}

func (boot bootstrap) comparePersistentDisk() error {
updateSettingsPath := filepath.Join(boot.platform.GetDirProvider().BoshDir(), "update_settings.json")

var updateSettings boshsettings.UpdateSettings

if boot.platform.GetFs().FileExists(updateSettingsPath) {
contents, err := boot.platform.GetFs().ReadFile(updateSettingsPath)
if err != nil {
return bosherr.WrapError(err, "Reading update_settings.json")
}

if err = json.Unmarshal(contents, &updateSettings); err != nil {
return bosherr.WrapError(err, "Unmarshalling update_settings.json")
}
}
updateSettings := boot.settingsService.GetSettings().UpdateSettings

for _, diskAssociation := range updateSettings.DiskAssociations {
_, err := boot.settingsService.GetPersistentDiskSettings(diskAssociation.DiskCID)
Expand Down
Loading

0 comments on commit 6afc068

Please sign in to comment.