2023-05-02 10:33:06 -05:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
2020-03-11 21:11:52 -05:00
|
|
|
package providercache
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
|
2023-08-17 07:45:11 -05:00
|
|
|
"github.com/placeholderplaceholderplaceholder/opentf/internal/addrs"
|
|
|
|
"github.com/placeholderplaceholderplaceholder/opentf/internal/getproviders"
|
2020-03-11 21:11:52 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
// 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.
|
2020-03-12 19:48:30 -05:00
|
|
|
PendingProviders func(reqs map[addrs.Provider]getproviders.VersionConstraints)
|
2020-03-11 21:11:52 -05:00
|
|
|
|
|
|
|
// 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.
|
2020-03-12 19:48:30 -05:00
|
|
|
//
|
|
|
|
// This event can also appear after the QueryPackages... series if
|
|
|
|
// querying determines that a version already available is the newest
|
|
|
|
// available version.
|
2020-03-11 21:11:52 -05:00
|
|
|
ProviderAlreadyInstalled func(provider addrs.Provider, selectedVersion getproviders.Version)
|
|
|
|
|
2020-04-01 18:44:50 -05:00
|
|
|
// The BuiltInProvider... family of events describe the outcome for any
|
|
|
|
// requested providers that are built in to Terraform. 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 Terraform. 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)
|
|
|
|
|
2020-03-11 21:11:52 -05:00
|
|
|
// 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.
|
2020-03-12 19:48:30 -05:00
|
|
|
//
|
|
|
|
// The Begin, Success, and Failure events will each occur only once per
|
2020-05-11 10:53:57 -05:00
|
|
|
// distinct provider.
|
2020-06-25 09:49:48 -05:00
|
|
|
//
|
|
|
|
// The Warning event is unique to the registry source
|
2020-10-02 19:00:49 -05:00
|
|
|
QueryPackagesBegin func(provider addrs.Provider, versionConstraints getproviders.VersionConstraints, locked bool)
|
2020-03-11 21:11:52 -05:00
|
|
|
QueryPackagesSuccess func(provider addrs.Provider, selectedVersion getproviders.Version)
|
|
|
|
QueryPackagesFailure func(provider addrs.Provider, err error)
|
2020-06-25 09:49:48 -05:00
|
|
|
QueryPackagesWarning func(provider addrs.Provider, warn []string)
|
2020-03-11 21:11:52 -05:00
|
|
|
|
|
|
|
// 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.
|
2020-03-12 19:48:30 -05:00
|
|
|
//
|
|
|
|
// The Query, Begin, Success, and Failure events will each occur only once
|
2020-05-11 10:53:57 -05:00
|
|
|
// per distinct provider.
|
2020-03-12 19:48:30 -05:00
|
|
|
FetchPackageMeta func(provider addrs.Provider, version getproviders.Version) // fetching metadata prior to real download
|
2020-03-11 21:11:52 -05:00
|
|
|
FetchPackageBegin func(provider addrs.Provider, version getproviders.Version, location getproviders.PackageLocation)
|
internal: Verify provider signatures on install
Providers installed from the registry are accompanied by a list of
checksums (the "SHA256SUMS" file), which is cryptographically signed to
allow package authentication. The process of verifying this has multiple
steps:
- First we must verify that the SHA256 hash of the package archive
matches the expected hash. This could be done for local installations
too, in the future.
- Next we ensure that the expected hash returned as part of the registry
API response matches an entry in the checksum list.
- Finally we verify the cryptographic signature of the checksum list,
using the public keys provided by the registry.
Each of these steps is implemented as a separate PackageAuthentication
type. The local archive installation mechanism uses only the archive
checksum authenticator, and the HTTP installation uses all three in the
order given.
The package authentication system now also returns a result value, which
is used by command/init to display the result of the authentication
process.
There are three tiers of signature, each of which is presented
differently to the user:
- Signatures from the embedded HashiCorp public key indicate that the
provider is officially supported by HashiCorp;
- If the signing key is not from HashiCorp, it may have an associated
trust signature, which indicates that the provider is from one of
HashiCorp's trusted partners;
- Otherwise, if the signature is valid, this is a community provider.
2020-04-08 15:22:07 -05:00
|
|
|
FetchPackageSuccess func(provider addrs.Provider, version getproviders.Version, localDir string, authResult *getproviders.PackageAuthenticationResult)
|
2020-03-11 21:11:52 -05:00
|
|
|
FetchPackageFailure func(provider addrs.Provider, version getproviders.Version, err error)
|
2020-03-27 18:50:04 -05:00
|
|
|
|
2022-07-20 07:28:04 -05:00
|
|
|
// 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)
|
|
|
|
|
2020-05-12 12:58:12 -05:00
|
|
|
// 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)
|
2020-03-11 21:11:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|