-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Provide smarter "plugin install/upgrade" for Central Repository Discovery #64
Provide smarter "plugin install/upgrade" for Central Repository Discovery #64
Conversation
ef07d40
to
3632732
Compare
I got some good feedback from @vuil and will rework some aspects of the PR to be more generic and facilitate testing at the same time. |
2b4efbf
to
22a8e16
Compare
This PR is now ready for review 😌 . It kind of exploded in size with the unit tests, but the non-test code changes only account for 32% of the PR. |
22a8e16
to
03945c6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some points to consider in-lined in the code.
} | ||
|
||
err = pluginmanager.UpgradePlugin(pluginName, pluginVersion, getTarget()) | ||
if err != nil { | ||
return err | ||
} | ||
log.Successf("successfully upgraded plugin '%s' to version '%s'", pluginName, pluginVersion) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "version" info is removed from this printout because with the more efficient SQL DB, we don't know which version we are upgrading too here, we just as to install the recommendedVersion
, whatever that version is.
This change in printout is not a big deal because it is preceded by another printout mentioning the version:
ℹ Installing plugin 'management-cluster:v0.0.1' with target 'mission-control'
✔ successfully upgraded plugin 'management-cluster'
args: []string{"plugin", "install", "--target", "invalid", "myplugin"}, | ||
expectedFailure: true, | ||
expectedErrorMsg: "invalid target specified. Please specify correct value of `--target` or `-t` flag from 'kubernetes/k8s/mission-control/tmc'", | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The above tests are only negative tests.
It is complicated to actually test installing a plugin from this part of the code, which I assume is why there was never a test for in this file.
// CreateDiscoveryFromV1alpha1 creates discovery interface from v1alpha1 API | ||
func CreateDiscoveryFromV1alpha1(pd configtypes.PluginDiscovery) (Discovery, error) { | ||
func CreateDiscoveryFromV1alpha1(pd configtypes.PluginDiscovery, criteria *PluginDiscoveryCriteria) (Discovery, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't love adding the criteria
here and not respecting it for any discovery other than OCI.
However, the go code using the discoveries does filter the plugin returned, so this is safe.
// extracted from the OCI image | ||
pluginDataDir string | ||
// inventory is the pluginInventory to be used by this discovery. | ||
inventory plugininventory.PluginInventory |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The use of this field allows the unit tests to change the inventory
to use a stub.
return pluginEntries, nil | ||
} | ||
|
||
// nolint: gocyclo |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This stub filtering function is too complex for the linter 😄
But I had spent enough time getting those tests to run, so I just disabled the linter for it.
This is why it is better to use SQL than our own Go logic for this smart filtering 😉
func verifyPluginPreDownload(p *discovery.Discovered) error { | ||
artifactInfo, err := p.Distribution.DescribeArtifact(p.RecommendedVersion, runtime.GOOS, runtime.GOARCH) | ||
func verifyPluginPreDownload(p *discovery.Discovered, version string) error { | ||
artifactInfo, err := p.Distribution.DescribeArtifact(version, runtime.GOOS, runtime.GOARCH) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@anujc25 Please pay attention to this change as it is not limited to the new Central Repo feature.
I believe it is safe, but I would appreciate your confirmation.
The reason is:
The verifyPluginPreDownload(..) function now verifies the repo using the requested version of the plugin, instead of always using the recommendedVersion. This should cause not change in functionality IIUC. It is required because with a more targeted query of plugins, the recommendedVersion artifact is not always fetched. For example, if installing an older version of the cluster plugin, say v0.26.0, the "smart" install will only fetch information about that version; so, if we were to attempt to verifyPluginPreDownload() using the RecommendedVersion of v0.28.0, that information would not be available, and would need to be fetched separately from the SQLite DB; so, instead, we just use the requested version.
return nil, err | ||
} | ||
return []configtypes.PluginDiscovery{pd}, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is the above block that uses the TANZU_CLI_PRE_RELEASE_REPO_IMAGE
variable.
If we comment this, we can successfully run the new Test_InstallPlugin_InstalledPlugins_Central_Repo
unit test.
// then the image prefix should be project.registry.vmware.com/tanzu-cli/plugins/ | ||
imagePrefix := path.Dir(image) | ||
// The data for the inventory is stored in the cache | ||
pluginDataDir := filepath.Join(common.DefaultCacheDir, inventoryDirName, name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Notice this change where the inventory SQLite DB is moved from:
~/.cache/_tanzu/plugin_inventory/plugin_inventory.db
to
~/.cache/_tanzu/plugin_inventory/<discoveryName>/plugin_inventory.db
This is to allow having multiple discovery sources using SQLite at the same time.
@anujc25 Does that fits with your publication approach?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. This seems reasonable.
03945c6
to
7ba64a4
Compare
if filter.Version != "" { | ||
if filter.Version == cli.VersionLatest { | ||
// We want the recommended version of the plugin | ||
whereClause = fmt.Sprintf("%s Version=RecommendedVersion AND", whereClause) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain this a bit? How will this condition be met?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question.
I coded this before I remembered that the Central Repo would not fill the RecommendedVersion
column at the beginning. Instead of removing it, I decided to prepare for when the RecommendedVersion
would be included in the Central Repo and leave it in.
However, if you look at the implementation of GetPlugins(..)
in that same file, you will notice that if filter.Version == cli.VersionLatest
, we compute the RecommendedVersion
and set it in filter.Version
.
So, currently, the code you are pointing out is actually dead code. Should we remove it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can keep it as it but please add a commit specifying the details and mention that this is a dead code that might be useful in the future.
7ba64a4
to
ea876be
Compare
The SQLite-backed OCI discovery now has "criteria" field which allows to only discover plugins that match that criteria. At the same time the PluginInventory now has a "PluginInventoryFilter" field which allows to only List() plugins respecting the filter. When using the PluginInventory, the OCI discovery converts its criteria into a PluginInventoryFilter to only request the proper plugins. Using this filtering system, it is now possible to limit the amount of fetched plugin data to a more targeted subset. Signed-off-by: Marc Khouzam <kmarc@vmware.com>
Signed-off-by: Marc Khouzam <kmarc@vmware.com>
Signed-off-by: Marc Khouzam <kmarc@vmware.com>
This new version of "plugin install" only obtains the required amount of information it needs to install a plugin. This avoids getting the entire list of plugins, which, with a Central Repository, can be large. A similar change was made for "plugin upgrade". Signed-off-by: Marc Khouzam <kmarc@vmware.com>
Signed-off-by: Marc Khouzam <kmarc@vmware.com>
Signed-off-by: Marc Khouzam <kmarc@vmware.com>
Signed-off-by: Marc Khouzam <kmarc@vmware.com>
ea876be
to
6a98d97
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Thanks for the detailed PR description and testing section.
Signed-off-by: Marc Khouzam <kmarc@vmware.com>
…very (#64) * Add filtering to the SQLite-backed OCI discovery The SQLite-backed OCI discovery now has "criteria" field which allows to only discover plugins that match that criteria. At the same time the PluginInventory now has a "PluginInventoryFilter" field which allows to only List() plugins respecting the filter. When using the PluginInventory, the OCI discovery converts its criteria into a PluginInventoryFilter to only request the proper plugins. Using this filtering system, it is now possible to limit the amount of fetched plugin data to a more targeted subset. The new "plugin install" now only obtains the required amount of information it needs to install a plugin. This avoids getting the entire list of plugins, which, with a Central Repository, can be large. A similar change was made for "plugin upgrade". Signed-off-by: Marc Khouzam <kmarc@vmware.com>
What this PR does / why we need it
When the Central Repo feature is enabled, we use an OCI discovery with an SQLite PluginInventory.
Before this PR, installing/upgrading a plugin was done by getting all plugins, and then looping through the list in Go to find the desired one.
This PR uses the SQLite DB to do a targeted search, and only fetch the information for the desired plugin by filtering by name, target, version, os and arch.
There are actually no user-visible changes due to this change: it is just an improvement on the efficiency of the implementation.
Actually, there is one user-visible change:
tz plugin install all
is no longer supported when the Central Repo feature is enabled. It does not make sense to intall all plugins present in the Central Repo. An error message is shown to the user if theall
argument is used (see testing section).To introduce this filtering functionality in a generic way, two generic filtering concepts are introduced, one for the
Discovery
(aDiscoveryCriteria
) and one for thePluginInventory
(aPluginInventoryFilter
). To clarify, this is what we use to install/upgrade plugin:So, with this PR:
PluginManager
specifies a newDiscoveryCriteria
when creating the OCI Discovery for the Central Repo. The criteria tells the Discovery what plugins should be discovered (by name, target, version, os, arch).DiscoveryCriteria
into a similarPluginInventoryFilter
, which it passes to thePluginInventory
PluginInventory
(which is an SQLite engine at the moment) uses thePluginInventoryFilter
to write specific SQL queries that respect thePluginInventoryFilter
.Describe testing done for PR
Release note
Additional information
manager_test.go
is disabled because it uses a local discovery which is not supported while we use the temporaryTANZU_CLI_PRE_RELEASE_REPO_IMAGE
variable to specify the discovery. If you disable the use ofTANZU_CLI_PRE_RELEASE_REPO_IMAGE
and use the list of discoveries in the config file, this new test does pass.RecommendedVersion
field empty for the moment.verifyPluginPreDownload(..)
function now verifies the repo using the requestedversion
of the plugin, instead of always using therecommendedVersion
. This should cause not change in functionality IIUC. It is required because with a more targeted query of plugins, the recommendedVersion artifact is not always fetched. For example, if installing an older version of thecluster
plugin, sayv0.26.0
, the "smart" install will only fetch information about that version; so, if we were to attempt toverifyPluginPreDownload()
using the RecommendedVersion ofv0.28.0
, that information would not be available, and would need to be fetched separately from the SQLite DB; so, instead, we just use the requested version.Special notes for your reviewer
As shown in the testing section, doing an efficient plugin discovery has some impacts.
We now differentiate between TMC and TKG plugins also using the version. So, if a user asks to install a specific version of a plugin without specifying the
--target
but specifying the--version
flag, and that only one of the two targets provides that version, we no longer ask the user to specify the target. E.g.,This is a rare case however, as we are aiming for users not to have to install plugins by versions themselves, but (eventually) use plugin groups.