Skip to content

Commit

Permalink
Merge pull request juju#18410 from SimonRichardson/pending-charm-watcher
Browse files Browse the repository at this point in the history
juju#18410

~~Requires juju#18409 to land first.~~

-----

Adds watching applications for pending charms so that they can be downloaded asynchronously. As we're watching new applications being added, we need to verify that they have (or still have) pending charms. The async downloader only watches for charms that are still pending.

This watcher is a strings watcher that emits application change UUIDs, and preserving the order of the events when either the application charm isn't pending or if the state returns a different order because of duplicate records. The code ensures the correct behaviour.

This is broken off from https://warthogs.atlassian.net/browse/JUJU-7166, as the PR was getting too large.

-----

This pull request includes several changes to the `domain/application/service/application.go` file, focusing on improving the handling of applications with pending charms and enhancing the test coverage. The most important changes include adding new methods and watchers, updating existing methods to improve readability, and adding new test cases.

Enhancements to application handling:

* [`domain/application/service/application.go`](diffhunk://#diff-40e92dad177289bb0953c58a7bf643aa27252c1125bdda8628c3b1029996c639L145-R166): Added a new method `GetApplicationsWithPendingCharmsFromUUIDs` to return applications with pending charms for specified UUIDs. [[1]](diffhunk://#diff-40e92dad177289bb0953c58a7bf643aa27252c1125bdda8628c3b1029996c639L145-R166) [[2]](diffhunk://#diff-40e92dad177289bb0953c58a7bf643aa27252c1125bdda8628c3b1029996c639R1356-R1365)
* [`domain/application/service/application.go`](diffhunk://#diff-40e92dad177289bb0953c58a7bf643aa27252c1125bdda8628c3b1029996c639R1514-R1590): Added a new watcher method `WatchApplicationsWithPendingCharms` to observe changes to applications with pending charms.
* [`domain/application/service/application.go`](diffhunk://#diff-40e92dad177289bb0953c58a7bf643aa27252c1125bdda8628c3b1029996c639R1602-R1606): Introduced a helper method `watchApplicationsWithPendingCharmsMapper` to filter and order changes for applications with pending charms.
* [`domain/application/service/application.go`](diffhunk://#diff-40e92dad177289bb0953c58a7bf643aa27252c1125bdda8628c3b1029996c639L65-R68): Updated comments in `AtomicApplicationState` interface for better readability and consistency. [[1]](diffhunk://#diff-40e92dad177289bb0953c58a7bf643aa27252c1125bdda8628c3b1029996c639L65-R68) [[2]](diffhunk://#diff-40e92dad177289bb0953c58a7bf643aa27252c1125bdda8628c3b1029996c639L86-R105) [[3]](diffhunk://#diff-40e92dad177289bb0953c58a7bf643aa27252c1125bdda8628c3b1029996c639L110-R121) [[4]](diffhunk://#diff-40e92dad177289bb0953c58a7bf643aa27252c1125bdda8628c3b1029996c639L124-R138)

Test coverage improvements:

* [`domain/application/service/application_test.go`](diffhunk://#diff-54808e512b799a9eb1000c96054f7135a8a0fa435bcd7ab6e36f8ee6ade75922R1145-R1369): Added new test cases to cover the new methods and watchers related to applications with pending charms. [[1]](diffhunk://#diff-54808e512b799a9eb1000c96054f7135a8a0fa435bcd7ab6e36f8ee6ade75922R1145-R1369) [[2]](diffhunk://#diff-54808e512b799a9eb1000c96054f7135a8a0fa435bcd7ab6e36f8ee6ade75922L1198-R1427)
* [`domain/application/service/application_test.go`](diffhunk://#diff-54808e512b799a9eb1000c96054f7135a8a0fa435bcd7ab6e36f8ee6ade75922R19-R26): Updated imports and setup mocks to support the new test cases. [[1]](diffhunk://#diff-54808e512b799a9eb1000c96054f7135a8a0fa435bcd7ab6e36f8ee6ade75922R19-R26) [[2]](diffhunk://#diff-54808e512b799a9eb1000c96054f7135a8a0fa435bcd7ab6e36f8ee6ade75922L40-L50) [[3]](diffhunk://#diff-54808e512b799a9eb1000c96054f7135a8a0fa435bcd7ab6e36f8ee6ade75922R8)


### QA Steps

This isn't currently wired up, but the tests should pass.

### Jira

https://warthogs.atlassian.net/browse/JUJU-7166
  • Loading branch information
jujubot authored Nov 27, 2024
2 parents 83fec01 + cf7d7e3 commit 06038f5
Show file tree
Hide file tree
Showing 20 changed files with 1,128 additions and 320 deletions.
74 changes: 51 additions & 23 deletions domain/application/service/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ type AtomicApplicationState interface {
GetUnitUUID(ctx domain.AtomicContext, unitName coreunit.Name) (coreunit.UUID, error)

// CreateApplication creates an application, returning an error satisfying
// [applicationerrors.ApplicationAlreadyExists] if the application already exists.
// If returns as error satisfying [applicationerrors.CharmNotFound] if the charm
// for the application is not found.
// [applicationerrors.ApplicationAlreadyExists] if the application already
// exists. If returns as error satisfying [applicationerrors.CharmNotFound]
// if the charm for the application is not found.
CreateApplication(domain.AtomicContext, string, application.AddApplicationArg) (coreapplication.ID, error)

// AddUnits adds the specified units to the application.
Expand All @@ -73,20 +73,25 @@ type AtomicApplicationState interface {
// SetUnitPassword updates the password for the specified unit UUID.
SetUnitPassword(domain.AtomicContext, coreunit.UUID, application.PasswordInfo) error

// SetCloudContainerStatus saves the given cloud container status, overwriting any current status data.
// If returns an error satisfying [applicationerrors.UnitNotFound] if the unit doesn't exist.
// SetCloudContainerStatus saves the given cloud container status,
// overwriting any current status data. If returns an error satisfying
// [applicationerrors.UnitNotFound] if the unit doesn't exist.
SetCloudContainerStatus(domain.AtomicContext, coreunit.UUID, application.CloudContainerStatusStatusInfo) error

// SetUnitAgentStatus saves the given unit agent status, overwriting any current status data.
// If returns an error satisfying [applicationerrors.UnitNotFound] if the unit doesn't exist.
// SetUnitAgentStatus saves the given unit agent status, overwriting any
// current status data. If returns an error satisfying
// [applicationerrors.UnitNotFound] if the unit doesn't exist.
SetUnitAgentStatus(domain.AtomicContext, coreunit.UUID, application.UnitAgentStatusInfo) error

// SetUnitWorkloadStatus saves the given unit workload status, overwriting any current status data.
// If returns an error satisfying [applicationerrors.UnitNotFound] if the unit doesn't exist.
// SetUnitWorkloadStatus saves the given unit workload status, overwriting
// any current status data. If returns an error satisfying
// [applicationerrors.UnitNotFound] if the unit doesn't exist.
SetUnitWorkloadStatus(domain.AtomicContext, coreunit.UUID, application.UnitWorkloadStatusInfo) error

// GetApplicationLife looks up the life of the specified application, returning an error
// satisfying [applicationerrors.ApplicationNotFoundError] if the application is not found.
// GetApplicationLife looks up the life of the specified application,
// returning an error satisfying
// [applicationerrors.ApplicationNotFoundError] if the application is not
// found.
GetApplicationLife(ctx domain.AtomicContext, appName string) (coreapplication.ID, life.Life, error)

// SetApplicationLife sets the life of the specified application.
Expand All @@ -97,11 +102,12 @@ type AtomicApplicationState interface {
// [applicationerrors.ApplicationNotFound] if the application is not found.
GetApplicationScaleState(domain.AtomicContext, coreapplication.ID) (application.ScaleState, error)

// SetApplicationScalingState sets the scaling details for the given caas application
// Scale is optional and is only set if not nil.
// SetApplicationScalingState sets the scaling details for the given caas
// application Scale is optional and is only set if not nil.
SetApplicationScalingState(ctx domain.AtomicContext, appID coreapplication.ID, scale *int, targetScale int, scaling bool) error

// SetDesiredApplicationScale updates the desired scale of the specified application.
// SetDesiredApplicationScale updates the desired scale of the specified
// application.
SetDesiredApplicationScale(domain.AtomicContext, coreapplication.ID, int) error

// GetUnitLife looks up the life of the specified unit, returning an error
Expand All @@ -111,9 +117,14 @@ type AtomicApplicationState interface {
// SetUnitLife sets the life of the specified unit.
SetUnitLife(domain.AtomicContext, coreunit.Name, life.Life) error

// InitialWatchStatementUnitLife returns the initial namespace query for the application unit life watcher.
// InitialWatchStatementUnitLife returns the initial namespace query for the
// application unit life watcher.
InitialWatchStatementUnitLife(appName string) (string, eventsource.NamespaceQuery)

// InitialWatchStatementApplicationsWithPendingCharms returns the initial
// namespace query for the applications with pending charms watcher.
InitialWatchStatementApplicationsWithPendingCharms() (string, eventsource.NamespaceQuery)

// DeleteApplication deletes the specified application, returning an error
// satisfying [applicationerrors.ApplicationNotFoundError] if the
// application doesn't exist. If the application still has units, as error
Expand All @@ -132,7 +143,8 @@ type AtomicApplicationState interface {
ctx domain.AtomicContext, unitName coreunit.Name,
) ([]*coresecrets.URI, error)

// GetSecretsForApplication returns the secrets owned by the specified application.
// GetSecretsForApplication returns the secrets owned by the specified
// application.
GetSecretsForApplication(
ctx domain.AtomicContext, applicationName string,
) ([]*coresecrets.URI, error)
Expand Down Expand Up @@ -200,6 +212,11 @@ type ApplicationState interface {
// application. Returns [applicationerrors.ApplicationNotFound] if the
// application is not found.
GetCharmModifiedVersion(ctx context.Context, id coreapplication.ID) (int, error)

// GetApplicationsWithPendingCharmsFromUUIDs returns the applications
// with pending charms for the specified UUIDs. If the application has a
// different status, it's ignored.
GetApplicationsWithPendingCharmsFromUUIDs(ctx context.Context, uuids []coreapplication.ID) ([]coreapplication.ID, error)
}

// DeleteSecretState describes methods used by the secret deleter plugin.
Expand Down Expand Up @@ -332,10 +349,11 @@ func makeCreateApplicationArgs(
}

return application.AddApplicationArg{
Charm: ch,
Platform: platformArg,
Origin: originArg,
Channel: channelArg,
Charm: ch,
Platform: platformArg,
Origin: originArg,
Channel: channelArg,
CharmStoragePath: args.CharmStoragePath,
}, nil
}

Expand Down Expand Up @@ -1162,9 +1180,9 @@ func (s *Service) SetApplicationScalingState(ctx context.Context, appName string

}

// GetApplicationScalingState returns the scale state of an application, returning an error
// satisfying [applicationerrors.ApplicationNotFoundError] if the application doesn't exist.
// This is used on CAAS models.
// GetApplicationScalingState returns the scale state of an application,
// returning an error satisfying [applicationerrors.ApplicationNotFoundError] if
// the application doesn't exist. This is used on CAAS models.
func (s *Service) GetApplicationScalingState(ctx context.Context, appName string) (ScalingState, error) {
var scaleState application.ScaleState
err := s.st.RunAtomic(ctx, func(ctx domain.AtomicContext) error {
Expand All @@ -1180,3 +1198,13 @@ func (s *Service) GetApplicationScalingState(ctx context.Context, appName string
Scaling: scaleState.Scaling,
}, errors.Trace(err)
}

// GetApplicationsWithPendingCharmsFromUUIDs returns the application UUIDs that
// have pending charms from the provided UUIDs. If there are no applications
// with pending status charms, then those applications are ignored.
func (s *Service) GetApplicationsWithPendingCharmsFromUUIDs(ctx context.Context, uuids []coreapplication.ID) ([]coreapplication.ID, error) {
if len(uuids) == 0 {
return nil, nil
}
return s.st.GetApplicationsWithPendingCharmsFromUUIDs(ctx, uuids)
}
Loading

0 comments on commit 06038f5

Please sign in to comment.