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)