// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 package providercache import ( "context" "github.com/opentofu/opentofu/internal/addrs" "github.com/opentofu/opentofu/internal/getproviders" ) // InstallerEvents is a collection of function references that can be // associated with an Installer object in order to be notified about various // installation lifecycle events during an install operation. // // The set of supported events is primarily motivated by allowing ongoing // progress reports in the UI of the command running provider installation, // and so this only exposes information interesting to display and does not // allow the recipient of the events to influence the ongoing process. // // Any of the fields may be left as nil to signal that the caller is not // interested in the associated event. It's better to leave a field set to // nil than to assign a do-nothing function into it because the installer // may choose to skip preparing certain temporary data structures if it can see // that a particular event is not used. type InstallerEvents struct { // The PendingProviders event is called prior to other events to give // the recipient prior notice of the full set of distinct provider // addresses it can expect to see mentioned in the other events. // // A recipient driving a UI might, for example, use this to pre-allocate // UI space for status reports for all of the providers and then update // those positions in-place as other events arrive. PendingProviders func(reqs map[addrs.Provider]getproviders.VersionConstraints) // ProviderAlreadyInstalled is called for any provider that was included // in PendingProviders but requires no further action because a suitable // version is already present in the local provider cache directory. // // This event can also appear after the QueryPackages... series if // querying determines that a version already available is the newest // available version. ProviderAlreadyInstalled func(provider addrs.Provider, selectedVersion getproviders.Version) // The BuiltInProvider... family of events describe the outcome for any // requested providers that are built in to OpenTofu. Only one of these // methods will be called for each such provider, and no other method // will be called for them except that they are included in the // aggregate PendingProviders map. // // The "Available" event reports that the requested builtin provider is // available in this release of OpenTofu. The "Failure" event reports // either that the provider is unavailable or that the request for it // is invalid somehow. BuiltInProviderAvailable func(provider addrs.Provider) BuiltInProviderFailure func(provider addrs.Provider, err error) // The QueryPackages... family of events delimit the operation of querying // a provider source for information about available packages matching // a particular version constraint, prior to selecting a single version // to install. // // A particular install operation includes only one query per distinct // provider, so a caller can use the provider argument as a unique // identifier to correlate between successive events. // // The Begin, Success, and Failure events will each occur only once per // distinct provider. // // The Warning event is unique to the registry source QueryPackagesBegin func(provider addrs.Provider, versionConstraints getproviders.VersionConstraints, locked bool) QueryPackagesSuccess func(provider addrs.Provider, selectedVersion getproviders.Version) QueryPackagesFailure func(provider addrs.Provider, err error) QueryPackagesWarning func(provider addrs.Provider, warn []string) // The LinkFromCache... family of events delimit the operation of linking // a selected provider package from the system-wide shared cache into the // current configuration's local cache. // // This sequence occurs instead of the FetchPackage... sequence if the // QueryPackages... sequence selects a version that is already in the // system-wide cache, and thus we will skip fetching it from the // originating provider source and take it from the shared cache instead. // // Linking should, in most cases, be a much faster operation than // fetching. However, it could still potentially be slow in some unusual // cases like a particularly large source package on a system where symlinks // are impossible, or when either of the cache directories are on a network // filesystem accessed over a slow link. LinkFromCacheBegin func(provider addrs.Provider, version getproviders.Version, cacheRoot string) LinkFromCacheSuccess func(provider addrs.Provider, version getproviders.Version, localDir string) LinkFromCacheFailure func(provider addrs.Provider, version getproviders.Version, err error) // The FetchPackage... family of events delimit the operation of retrieving // a package from a particular source location. // // A particular install operation includes only one fetch per distinct // provider, so a caller can use the provider argument as a unique // identifier to correlate between successive events. // // A particular provider will either notify the LinkFromCache... events // or the FetchPackage... events, never both in the same install operation. // // The Query, Begin, Success, and Failure events will each occur only once // per distinct provider. FetchPackageMeta func(provider addrs.Provider, version getproviders.Version) // fetching metadata prior to real download FetchPackageBegin func(provider addrs.Provider, version getproviders.Version, location getproviders.PackageLocation) FetchPackageSuccess func(provider addrs.Provider, version getproviders.Version, localDir string, authResult *getproviders.PackageAuthenticationResult) FetchPackageFailure func(provider addrs.Provider, version getproviders.Version, err error) // The ProvidersLockUpdated event is called whenever the lock file will be // updated. It provides the following information: // // - `localHashes`: Hashes computed on the local system by analyzing // files on disk. // - `signedHashes`: Hashes signed by the private key that the origin // registry claims is the owner of this provider. // - `priorHashes`: Hashes already present in the lock file before we // made any changes. // // The final lock file will be updated with a union of all the provided // hashes. It not just likely, but expected that there will be duplicates // shared between all three collections of hashes i.e. the local hash and // remote hashes could already be in the cached hashes. // // In addition, we place a guarantee that the hash slices will be ordered // in the same manner enforced by the lock file within NewProviderLock. ProvidersLockUpdated func(provider addrs.Provider, version getproviders.Version, localHashes []getproviders.Hash, signedHashes []getproviders.Hash, priorHashes []getproviders.Hash) // The ProvidersFetched event is called after all fetch operations if at // least one provider was fetched successfully. ProvidersFetched func(authResults map[addrs.Provider]*getproviders.PackageAuthenticationResult) } // OnContext produces a context with all of the same behaviors as the given // context except that it will additionally carry the receiving // InstallerEvents. // // Passing the resulting context to an installer request will cause the // installer to send event notifications via the callbacks inside. func (e *InstallerEvents) OnContext(ctx context.Context) context.Context { return context.WithValue(ctx, ctxInstallerEvents, e) } // installerEventsForContext looks on the given context for a registered // InstallerEvents and returns a pointer to it if so. // // For caller convenience, if there is no events object attached to the // given context this function will construct one that has all of its // fields set to nil and return that, freeing the caller from having to // do a nil check on the result before dereferencing it. func installerEventsForContext(ctx context.Context) *InstallerEvents { v := ctx.Value(ctxInstallerEvents) if v != nil { return v.(*InstallerEvents) } return &InstallerEvents{} } type ctxInstallerEventsType int const ctxInstallerEvents = ctxInstallerEventsType(0)