From 03155daf98b4f2139a870d6c3ed346cc54c26f30 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Wed, 11 Mar 2020 19:11:52 -0700 Subject: [PATCH] internal/providercache: Start to stub Installer type The Installer type will encapsulate the logic for running an entire provider installation request: given a set of providers to install, it will determine a method to obtain each of them (or detect that they are already installed) and then take the necessary actions. So far it doesn't do anything, but this stubs out an interface by which the caller can request ongoing notifications during an installation operation. --- internal/getproviders/types.go | 5 + internal/providercache/installer.go | 7 ++ internal/providercache/installer_events.go | 110 +++++++++++++++++++++ 3 files changed, 122 insertions(+) create mode 100644 internal/providercache/installer.go create mode 100644 internal/providercache/installer_events.go diff --git a/internal/getproviders/types.go b/internal/getproviders/types.go index c675cd007..0af6ebd83 100644 --- a/internal/getproviders/types.go +++ b/internal/getproviders/types.go @@ -18,6 +18,11 @@ type Version = versions.Version // extra methods for convenient filtering. type VersionList = versions.List +// VersionSet represents a set of versions, usually describing the acceptable +// versions that can be selected under a particular version constraint provided +// by the end-user. +type VersionSet = versions.Set + // ParseVersion parses a "semver"-style version string into a Version value, // which is the version syntax we use for provider versions. func ParseVersion(str string) (Version, error) { diff --git a/internal/providercache/installer.go b/internal/providercache/installer.go new file mode 100644 index 000000000..903d9b614 --- /dev/null +++ b/internal/providercache/installer.go @@ -0,0 +1,7 @@ +package providercache + +// Installer is the main type in this package, representing a provider installer +// with a particular configuration-specific cache directory and an optional +// global cache directory. +type Installer struct { +} diff --git a/internal/providercache/installer_events.go b/internal/providercache/installer_events.go new file mode 100644 index 000000000..c02d5bd93 --- /dev/null +++ b/internal/providercache/installer_events.go @@ -0,0 +1,110 @@ +package providercache + +import ( + "context" + + "github.com/hashicorp/terraform/addrs" + "github.com/hashicorp/terraform/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(provider addrs.Provider) + + // 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. + ProviderAlreadyInstalled func(provider addrs.Provider, selectedVersion getproviders.Version) + + // 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. + QueryPackagesBegin func(provider addrs.Provider, versionSet getproviders.VersionSet) + QueryPackagesSuccess func(provider addrs.Provider, selectedVersion getproviders.Version) + QueryPackagesFailure func(provider addrs.Provider, err error) + + // 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. + FetchPackageBegin func(provider addrs.Provider, version getproviders.Version, location getproviders.PackageLocation) + FetchPackageSuccess func(provider addrs.Provider, version getproviders.Version, localDir string) + FetchPackageFailure func(provider addrs.Provider, version getproviders.Version, err error) +} + +// 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)