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
43 changes: 31 additions & 12 deletions db/sqldb/desired_lrp_db.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func (db *SQLDB) DesiredLRPByProcessGuid(ctx context.Context, logger lager.Logge
"process_guid = ?", processGuid,
)

desiredLRP, err = db.fetchDesiredLRP(ctx, logger, row, tx)
desiredLRP, _, err = db.fetchDesiredLRP(ctx, logger, row, tx)
return err
})

Expand Down Expand Up @@ -300,13 +300,14 @@ func (db *SQLDB) UpdateDesiredLRP(ctx context.Context, logger lager.Logger, proc
defer logger.Info("complete")

var beforeDesiredLRP *models.DesiredLRP
var originalRunInfo *models.DesiredLRPRunInfo
err := db.transact(ctx, logger, func(logger lager.Logger, tx helpers.Tx) error {
var err error
row := db.one(ctx, logger, tx, desiredLRPsTable,
desiredLRPColumns, helpers.LockRow,
"process_guid = ?", processGuid,
)
beforeDesiredLRP, err = db.fetchDesiredLRP(ctx, logger, row, tx)
beforeDesiredLRP, originalRunInfo, err = db.fetchDesiredLRP(ctx, logger, row, tx)

if err != nil {
logger.Error("failed-lock-desired", err)
Expand Down Expand Up @@ -339,6 +340,24 @@ func (db *SQLDB) UpdateDesiredLRP(ctx context.Context, logger lager.Logger, proc
updateAttributes["metric_tags"] = encodedData
}

if update.ImageUsernameExists() || update.ImagePasswordExists() {
runInfo := *originalRunInfo

if update.ImageUsernameExists() {
runInfo.ImageUsername = update.GetImageUsername()
}
if update.ImagePasswordExists() {
runInfo.ImagePassword = update.GetImagePassword()
}

updatedRunInfoData, err := db.serializeModel(logger, &runInfo)
if err != nil {
logger.Error("failed-serializing-run-info", err)
return err
}
updateAttributes["run_info"] = updatedRunInfoData
}

_, err = db.update(ctx, logger, tx, desiredLRPsTable, updateAttributes, `process_guid = ?`, processGuid)
if err != nil {
logger.Error("failed-executing-query", err)
Expand Down Expand Up @@ -536,7 +555,7 @@ func (db *SQLDB) fetchDesiredLRPs(ctx context.Context, logger lager.Logger, rows
guids := []string{}
lrps := []*models.DesiredLRP{}
for rows.Next() {
lrp, guid, err := db.fetchDesiredLRPInternal(logger, rows)
lrp, _, guid, err := db.fetchDesiredLRPInternal(logger, rows)
if err == models.ErrDeserialize {
guids = append(guids, guid)
}
Expand All @@ -561,28 +580,28 @@ func (db *SQLDB) fetchDesiredLRPs(ctx context.Context, logger lager.Logger, rows
return lrps, nil
}

func (db *SQLDB) fetchDesiredLRP(ctx context.Context, logger lager.Logger, scanner helpers.RowScanner, queryable helpers.Queryable) (*models.DesiredLRP, error) {
lrp, guid, err := db.fetchDesiredLRPInternal(logger, scanner)
func (db *SQLDB) fetchDesiredLRP(ctx context.Context, logger lager.Logger, scanner helpers.RowScanner, queryable helpers.Queryable) (*models.DesiredLRP, *models.DesiredLRPRunInfo, error) {
lrp, runInfo, guid, err := db.fetchDesiredLRPInternal(logger, scanner)
if err == models.ErrDeserialize {
deleteErr := db.deleteInvalidLRPs(ctx, logger, queryable, guid)
if deleteErr != nil {
logger.Error("failed-to-delete-invalid-lrp", deleteErr, lager.Data{"guid": guid})
}
}
return lrp, err
return lrp, runInfo, err
}

func (db *SQLDB) fetchDesiredLRPInternal(logger lager.Logger, scanner helpers.RowScanner) (*models.DesiredLRP, string, error) {
func (db *SQLDB) fetchDesiredLRPInternal(logger lager.Logger, scanner helpers.RowScanner) (*models.DesiredLRP, *models.DesiredLRPRunInfo, string, error) {
var runInfoData, metricTagsData []byte
schedulingInfo, err := db.fetchDesiredLRPSchedulingInfoAndMore(logger, scanner, &runInfoData, &metricTagsData)
if err != nil {
return nil, "", err
return nil, nil, "", err
}

var runInfo models.DesiredLRPRunInfo
err = db.deserializeModel(logger, runInfoData, &runInfo)
if err != nil {
return nil, schedulingInfo.ProcessGuid, models.ErrDeserialize
return nil, nil, schedulingInfo.ProcessGuid, models.ErrDeserialize
}
// dedup the ports
runInfo.Ports = dedupSlice(runInfo.Ports)
Expand All @@ -591,15 +610,15 @@ func (db *SQLDB) fetchDesiredLRPInternal(logger lager.Logger, scanner helpers.Ro
encodedData, err := db.encoder.Decode(metricTagsData)
if err != nil {
logger.Error("failed-decrypting-metric-tags", err)
return nil, "", err
return nil, nil, "", err
}
err = json.Unmarshal(encodedData, &metricTags)
if err != nil {
logger.Error("failed-parsing-metric-tags", err)
return nil, "", err
return nil, nil, "", err
}
desiredLRP := models.NewDesiredLRP(*schedulingInfo, runInfo, metricTags)
return &desiredLRP, "", nil
return &desiredLRP, &runInfo, "", nil
}

func (db *SQLDB) deleteInvalidLRPs(ctx context.Context, logger lager.Logger, queryable helpers.Queryable, guids ...string) error {
Expand Down
81 changes: 81 additions & 0 deletions db/sqldb/desired_lrp_db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,87 @@ var _ = Describe("DesiredLRPDB", func() {
})
})

Context("when updating image credentials", func() {
BeforeEach(func() {
expectedDesiredLRP.ImageUsername = "original-username"
expectedDesiredLRP.ImagePassword = "original-password"
expectedDesiredLRP.CpuWeight = 42
})

It("updates both image username and password in run_info and maintains other fields", func() {
originalAction := expectedDesiredLRP.Action
originalMonitor := expectedDesiredLRP.Monitor
originalStartTimeout := expectedDesiredLRP.StartTimeoutMs
update = &models.DesiredLRPUpdate{}
update.SetImageUsername("new-username")
update.SetImagePassword("new-password")

_, err := sqlDB.UpdateDesiredLRP(ctx, logger, expectedDesiredLRP.ProcessGuid, update)
Expect(err).NotTo(HaveOccurred())

desiredLRP, err := sqlDB.DesiredLRPByProcessGuid(ctx, logger, expectedDesiredLRP.ProcessGuid)
Expect(err).NotTo(HaveOccurred())

Expect(desiredLRP.ImageUsername).To(Equal("new-username"))
Expect(desiredLRP.ImagePassword).To(Equal("new-password"))
Expect(desiredLRP.CpuWeight).To(Equal(uint32(42)))
expectedDesiredLRP.ImageUsername = "new-username"
expectedDesiredLRP.ImagePassword = "new-password"
expectedDesiredLRP.ModificationTag.Increment()

Expect(desiredLRP).To(BeEquivalentTo(expectedDesiredLRP))
Expect(desiredLRP.Action).To(Equal(originalAction))
Expect(desiredLRP.Monitor).To(Equal(originalMonitor))
Expect(desiredLRP.StartTimeoutMs).To(Equal(originalStartTimeout))
})

It("updates only image username and preserves password", func() {
originalAction := expectedDesiredLRP.Action
originalMonitor := expectedDesiredLRP.Monitor
update = &models.DesiredLRPUpdate{}
update.SetImageUsername("new-username-only")

_, err := sqlDB.UpdateDesiredLRP(ctx, logger, expectedDesiredLRP.ProcessGuid, update)
Expect(err).NotTo(HaveOccurred())

desiredLRP, err := sqlDB.DesiredLRPByProcessGuid(ctx, logger, expectedDesiredLRP.ProcessGuid)
Expect(err).NotTo(HaveOccurred())

Expect(desiredLRP.ImageUsername).To(Equal("new-username-only"))
Expect(desiredLRP.ImagePassword).To(Equal("original-password"))
Expect(desiredLRP.CpuWeight).To(Equal(uint32(42)))
expectedDesiredLRP.ImageUsername = "new-username-only"
expectedDesiredLRP.ModificationTag.Increment()

Expect(desiredLRP).To(BeEquivalentTo(expectedDesiredLRP))
Expect(desiredLRP.Action).To(Equal(originalAction))
Expect(desiredLRP.Monitor).To(Equal(originalMonitor))
})

It("updates only image password and preserves username", func() {
originalAction := expectedDesiredLRP.Action
originalMonitor := expectedDesiredLRP.Monitor
update = &models.DesiredLRPUpdate{}
update.SetImagePassword("new-password-only")

_, err := sqlDB.UpdateDesiredLRP(ctx, logger, expectedDesiredLRP.ProcessGuid, update)
Expect(err).NotTo(HaveOccurred())

desiredLRP, err := sqlDB.DesiredLRPByProcessGuid(ctx, logger, expectedDesiredLRP.ProcessGuid)
Expect(err).NotTo(HaveOccurred())

Expect(desiredLRP.ImageUsername).To(Equal("original-username"))
Expect(desiredLRP.ImagePassword).To(Equal("new-password-only"))
Expect(desiredLRP.CpuWeight).To(Equal(uint32(42)))
expectedDesiredLRP.ImagePassword = "new-password-only"
expectedDesiredLRP.ModificationTag.Increment()

Expect(desiredLRP).To(BeEquivalentTo(expectedDesiredLRP))
Expect(desiredLRP.Action).To(Equal(originalAction))
Expect(desiredLRP.Monitor).To(Equal(originalMonitor))
})
})

Context("when routes param is invalid", func() {
It("returns a bad request error", func() {
routeContent := []byte("bad json")
Expand Down
74 changes: 70 additions & 4 deletions models/desired_lrp.go
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,16 @@ func (desired *DesiredLRPUpdate) Validate() error {
validationError = validationError.Append(err)
}

// Validate that image_username and image_password are provided together
usernameExists := desired.ImageUsernameExists()
passwordExists := desired.ImagePasswordExists()
if usernameExists && !passwordExists {
validationError = validationError.Append(ErrInvalidField{"image_password"})
}
if !usernameExists && passwordExists {
validationError = validationError.Append(ErrInvalidField{"image_username"})
}

return validationError.ToError()
}

Expand Down Expand Up @@ -493,11 +503,53 @@ func (desired DesiredLRPUpdate) IsMetricTagsUpdated(existingTags map[string]*Met
return false
}

func (desired *DesiredLRPUpdate) SetImageUsername(imageUsername string) {
desired.OptionalImageUsername = &DesiredLRPUpdate_ImageUsername{
ImageUsername: imageUsername,
}
}

func (desired DesiredLRPUpdate) ImageUsernameExists() bool {
_, ok := desired.GetOptionalImageUsername().(*DesiredLRPUpdate_ImageUsername)
return ok
}

func (desired *DesiredLRPUpdate) SetImagePassword(imagePassword string) {
desired.OptionalImagePassword = &DesiredLRPUpdate_ImagePassword{
ImagePassword: imagePassword,
}
}

func (desired DesiredLRPUpdate) ImagePasswordExists() bool {
_, ok := desired.GetOptionalImagePassword().(*DesiredLRPUpdate_ImagePassword)
return ok
}

func (desired DesiredLRPUpdate) IsImageCredentialsUpdated(existingLRP *DesiredLRP) bool {
updateUsername := desired.ImageUsernameExists()
updatePassword := desired.ImagePasswordExists()

if !updateUsername && !updatePassword {
return false
}

existingUsername := existingLRP.GetImageUsername()
existingPassword := existingLRP.GetImagePassword()

if updateUsername && updatePassword {
return desired.GetImageUsername() != existingUsername || desired.GetImagePassword() != existingPassword
}

return false
}

type internalDesiredLRPUpdate struct {
Instances *int32 `json:"instances,omitempty"`
Routes *Routes `json:"routes,omitempty"`
Annotation *string `json:"annotation,omitempty"`
MetricTags map[string]*MetricTagValue `json:"metric_tags,omitempty"`
Instances *int32 `json:"instances,omitempty"`
Routes *Routes `json:"routes,omitempty"`
Annotation *string `json:"annotation,omitempty"`
MetricTags map[string]*MetricTagValue `json:"metric_tags,omitempty"`
ImageUsername *string `json:"image_username,omitempty"`
ImagePassword *string `json:"image_password,omitempty"`
}

func (desired *DesiredLRPUpdate) UnmarshalJSON(data []byte) error {
Expand All @@ -514,6 +566,12 @@ func (desired *DesiredLRPUpdate) UnmarshalJSON(data []byte) error {
desired.SetAnnotation(*update.Annotation)
}
desired.MetricTags = update.MetricTags
if update.ImageUsername != nil {
desired.SetImageUsername(*update.ImageUsername)
}
if update.ImagePassword != nil {
desired.SetImagePassword(*update.ImagePassword)
}

return nil
}
Expand All @@ -530,6 +588,14 @@ func (desired DesiredLRPUpdate) MarshalJSON() ([]byte, error) {
update.Annotation = &a
}
update.MetricTags = desired.MetricTags
if desired.ImageUsernameExists() {
username := desired.GetImageUsername()
update.ImageUsername = &username
}
if desired.ImagePasswordExists() {
password := desired.GetImagePassword()
update.ImagePassword = &password
}
return json.Marshal(update)
}

Expand Down
Loading