github.com/opentofu/opentofu@v1.7.1/internal/providercache/installer_events.go (about) 1 // Copyright (c) The OpenTofu Authors 2 // SPDX-License-Identifier: MPL-2.0 3 // Copyright (c) 2023 HashiCorp, Inc. 4 // SPDX-License-Identifier: MPL-2.0 5 6 package providercache 7 8 import ( 9 "context" 10 11 "github.com/opentofu/opentofu/internal/addrs" 12 "github.com/opentofu/opentofu/internal/getproviders" 13 ) 14 15 // InstallerEvents is a collection of function references that can be 16 // associated with an Installer object in order to be notified about various 17 // installation lifecycle events during an install operation. 18 // 19 // The set of supported events is primarily motivated by allowing ongoing 20 // progress reports in the UI of the command running provider installation, 21 // and so this only exposes information interesting to display and does not 22 // allow the recipient of the events to influence the ongoing process. 23 // 24 // Any of the fields may be left as nil to signal that the caller is not 25 // interested in the associated event. It's better to leave a field set to 26 // nil than to assign a do-nothing function into it because the installer 27 // may choose to skip preparing certain temporary data structures if it can see 28 // that a particular event is not used. 29 type InstallerEvents struct { 30 // The PendingProviders event is called prior to other events to give 31 // the recipient prior notice of the full set of distinct provider 32 // addresses it can expect to see mentioned in the other events. 33 // 34 // A recipient driving a UI might, for example, use this to pre-allocate 35 // UI space for status reports for all of the providers and then update 36 // those positions in-place as other events arrive. 37 PendingProviders func(reqs map[addrs.Provider]getproviders.VersionConstraints) 38 39 // ProviderAlreadyInstalled is called for any provider that was included 40 // in PendingProviders but requires no further action because a suitable 41 // version is already present in the local provider cache directory. 42 // 43 // This event can also appear after the QueryPackages... series if 44 // querying determines that a version already available is the newest 45 // available version. 46 ProviderAlreadyInstalled func(provider addrs.Provider, selectedVersion getproviders.Version) 47 48 // The BuiltInProvider... family of events describe the outcome for any 49 // requested providers that are built in to OpenTofu. Only one of these 50 // methods will be called for each such provider, and no other method 51 // will be called for them except that they are included in the 52 // aggregate PendingProviders map. 53 // 54 // The "Available" event reports that the requested builtin provider is 55 // available in this release of OpenTofu. The "Failure" event reports 56 // either that the provider is unavailable or that the request for it 57 // is invalid somehow. 58 BuiltInProviderAvailable func(provider addrs.Provider) 59 BuiltInProviderFailure func(provider addrs.Provider, err error) 60 61 // The QueryPackages... family of events delimit the operation of querying 62 // a provider source for information about available packages matching 63 // a particular version constraint, prior to selecting a single version 64 // to install. 65 // 66 // A particular install operation includes only one query per distinct 67 // provider, so a caller can use the provider argument as a unique 68 // identifier to correlate between successive events. 69 // 70 // The Begin, Success, and Failure events will each occur only once per 71 // distinct provider. 72 // 73 // The Warning event is unique to the registry source 74 QueryPackagesBegin func(provider addrs.Provider, versionConstraints getproviders.VersionConstraints, locked bool) 75 QueryPackagesSuccess func(provider addrs.Provider, selectedVersion getproviders.Version) 76 QueryPackagesFailure func(provider addrs.Provider, err error) 77 QueryPackagesWarning func(provider addrs.Provider, warn []string) 78 79 // The LinkFromCache... family of events delimit the operation of linking 80 // a selected provider package from the system-wide shared cache into the 81 // current configuration's local cache. 82 // 83 // This sequence occurs instead of the FetchPackage... sequence if the 84 // QueryPackages... sequence selects a version that is already in the 85 // system-wide cache, and thus we will skip fetching it from the 86 // originating provider source and take it from the shared cache instead. 87 // 88 // Linking should, in most cases, be a much faster operation than 89 // fetching. However, it could still potentially be slow in some unusual 90 // cases like a particularly large source package on a system where symlinks 91 // are impossible, or when either of the cache directories are on a network 92 // filesystem accessed over a slow link. 93 LinkFromCacheBegin func(provider addrs.Provider, version getproviders.Version, cacheRoot string) 94 LinkFromCacheSuccess func(provider addrs.Provider, version getproviders.Version, localDir string) 95 LinkFromCacheFailure func(provider addrs.Provider, version getproviders.Version, err error) 96 97 // The FetchPackage... family of events delimit the operation of retrieving 98 // a package from a particular source location. 99 // 100 // A particular install operation includes only one fetch per distinct 101 // provider, so a caller can use the provider argument as a unique 102 // identifier to correlate between successive events. 103 // 104 // A particular provider will either notify the LinkFromCache... events 105 // or the FetchPackage... events, never both in the same install operation. 106 // 107 // The Query, Begin, Success, and Failure events will each occur only once 108 // per distinct provider. 109 FetchPackageMeta func(provider addrs.Provider, version getproviders.Version) // fetching metadata prior to real download 110 FetchPackageBegin func(provider addrs.Provider, version getproviders.Version, location getproviders.PackageLocation) 111 FetchPackageSuccess func(provider addrs.Provider, version getproviders.Version, localDir string, authResult *getproviders.PackageAuthenticationResult) 112 FetchPackageFailure func(provider addrs.Provider, version getproviders.Version, err error) 113 114 // The ProvidersLockUpdated event is called whenever the lock file will be 115 // updated. It provides the following information: 116 // 117 // - `localHashes`: Hashes computed on the local system by analyzing 118 // files on disk. 119 // - `signedHashes`: Hashes signed by the private key that the origin 120 // registry claims is the owner of this provider. 121 // - `priorHashes`: Hashes already present in the lock file before we 122 // made any changes. 123 // 124 // The final lock file will be updated with a union of all the provided 125 // hashes. It not just likely, but expected that there will be duplicates 126 // shared between all three collections of hashes i.e. the local hash and 127 // remote hashes could already be in the cached hashes. 128 // 129 // In addition, we place a guarantee that the hash slices will be ordered 130 // in the same manner enforced by the lock file within NewProviderLock. 131 ProvidersLockUpdated func(provider addrs.Provider, version getproviders.Version, localHashes []getproviders.Hash, signedHashes []getproviders.Hash, priorHashes []getproviders.Hash) 132 133 // The ProvidersFetched event is called after all fetch operations if at 134 // least one provider was fetched successfully. 135 ProvidersFetched func(authResults map[addrs.Provider]*getproviders.PackageAuthenticationResult) 136 } 137 138 // OnContext produces a context with all of the same behaviors as the given 139 // context except that it will additionally carry the receiving 140 // InstallerEvents. 141 // 142 // Passing the resulting context to an installer request will cause the 143 // installer to send event notifications via the callbacks inside. 144 func (e *InstallerEvents) OnContext(ctx context.Context) context.Context { 145 return context.WithValue(ctx, ctxInstallerEvents, e) 146 } 147 148 // installerEventsForContext looks on the given context for a registered 149 // InstallerEvents and returns a pointer to it if so. 150 // 151 // For caller convenience, if there is no events object attached to the 152 // given context this function will construct one that has all of its 153 // fields set to nil and return that, freeing the caller from having to 154 // do a nil check on the result before dereferencing it. 155 func installerEventsForContext(ctx context.Context) *InstallerEvents { 156 v := ctx.Value(ctxInstallerEvents) 157 if v != nil { 158 return v.(*InstallerEvents) 159 } 160 return &InstallerEvents{} 161 } 162 163 type ctxInstallerEventsType int 164 165 const ctxInstallerEvents = ctxInstallerEventsType(0)