-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Implement PluginVersionWarner to warn on outdated plugins. (#419)
* Implement PluginVersionWarner for unauthenticated version checking. * Fix rebase conflicts and previously merged version strategy. * Make sure it doesn't panic if called after bad init. * Improve safeguard for nil struct. * Make kind a string, because there's no PluginKind.FromString. * Update tests. * Use constants. * Optionally accept an AuthToken.
- Loading branch information
1 parent
a5f68a1
commit dc7f2bd
Showing
5 changed files
with
126 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package managedplugin | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/Masterminds/semver" | ||
cloudquery_api "github.com/cloudquery/cloudquery-api-go" | ||
"github.com/rs/zerolog" | ||
) | ||
|
||
type PluginVersionWarner struct { | ||
hubClient *cloudquery_api.ClientWithResponses | ||
logger zerolog.Logger | ||
} | ||
|
||
func NewPluginVersionWarner(logger zerolog.Logger, optionalAuthToken string) (*PluginVersionWarner, error) { | ||
hubClient, err := getHubClient(logger, HubDownloadOptions{AuthToken: optionalAuthToken}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &PluginVersionWarner{hubClient: hubClient, logger: logger}, nil | ||
} | ||
|
||
func (p *PluginVersionWarner) getLatestVersion(ctx context.Context, org string, name string, kind string) (*semver.Version, error) { | ||
if p == nil { | ||
return nil, fmt.Errorf("plugin version warner is not initialized") | ||
} | ||
if kind != PluginSource.String() && kind != PluginDestination.String() && kind != PluginTransformer.String() { | ||
p.logger.Debug().Str("plugin", name).Str("kind", kind).Msg("invalid kind") | ||
return nil, fmt.Errorf("invalid kind: %s", kind) | ||
} | ||
resp, err := p.hubClient.GetPluginWithResponse(ctx, org, cloudquery_api.PluginKind(kind), name) | ||
if err != nil { | ||
p.logger.Debug().Str("plugin", name).Err(err).Msg("failed to get plugin info from hub") | ||
return nil, err | ||
} | ||
if resp.JSON200 == nil { | ||
p.logger.Debug().Str("plugin", name).Msg("failed to get plugin info from hub, request didn't error but 200 response is nil") | ||
return nil, fmt.Errorf("failed to get plugin info from hub, request didn't error but 200 response is nil") | ||
} | ||
if resp.JSON200.LatestVersion == nil { | ||
p.logger.Debug().Str("plugin", name).Msg("cannot check if plugin is outdated, latest version is nil") | ||
return nil, fmt.Errorf("cannot check if plugin is outdated, latest version is nil") | ||
} | ||
latestVersion := *resp.JSON200.LatestVersion | ||
latestSemver, err := semver.NewVersion(latestVersion) | ||
if err != nil { | ||
p.logger.Debug().Str("plugin", name).Str("version", latestVersion).Err(err).Msg("failed to parse latest version") | ||
return nil, err | ||
} | ||
return latestSemver, nil | ||
} | ||
|
||
// WarnIfOutdated requests the latest version of a plugin from the hub and warns if the client's supplied version is outdated. | ||
// It returns true if nothing went wrong comparing the versions, and the client's version is outdated; false otherwise. | ||
func (p *PluginVersionWarner) WarnIfOutdated(ctx context.Context, org string, name string, kind string, actualVersion string) (bool, error) { | ||
if p == nil { | ||
return false, fmt.Errorf("plugin version warner is not initialized") | ||
} | ||
if actualVersion == "" { | ||
return false, nil | ||
} | ||
actualVersionSemver, err := semver.NewVersion(actualVersion) | ||
if err != nil { | ||
p.logger.Debug().Str("plugin", name).Str("version", actualVersion).Err(err).Msg("failed to parse actual version") | ||
return false, err | ||
} | ||
latestVersionSemver, err := p.getLatestVersion(ctx, org, name, kind) | ||
if err != nil { | ||
return false, err | ||
} | ||
if actualVersionSemver.LessThan(latestVersionSemver) { | ||
p.logger.Warn(). | ||
Str("plugin", name). | ||
Str("using_version", actualVersionSemver.String()). | ||
Str("latest_version", latestVersionSemver.String()). | ||
Str("url", fmt.Sprintf("https://hub.cloudquery.io/plugins/%s/%s/%s", kind, org, name)). | ||
Msg("Plugin is outdated, consider upgrading to the latest version.") | ||
return true, nil | ||
} | ||
|
||
return false, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package managedplugin | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/rs/zerolog" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestPluginVersionWarnerUnknownPluginFails(t *testing.T) { | ||
versionWarner, err := NewPluginVersionWarner(zerolog.Nop(), "") | ||
require.NoError(t, err) | ||
warned, err := versionWarner.WarnIfOutdated(context.Background(), "unknown", "unknown", "source", "1.0.0") | ||
assert.Error(t, err) | ||
assert.False(t, warned) | ||
} | ||
|
||
// Note: this is an integration test that requires Internet access and the hub to be running | ||
func TestPluginLatestVersionDoesNotWarn(t *testing.T) { | ||
versionWarner, err := NewPluginVersionWarner(zerolog.Nop(), "") | ||
require.NoError(t, err) | ||
latestVersion, err := versionWarner.getLatestVersion(context.Background(), "cloudquery", "aws", "source") | ||
assert.NoError(t, err) | ||
hasWarned, err := versionWarner.WarnIfOutdated(context.Background(), "cloudquery", "aws", "source", latestVersion.String()) | ||
assert.NoError(t, err) | ||
assert.False(t, hasWarned) | ||
} | ||
|
||
// Note: this is an integration test that requires Internet access and the hub to be running | ||
// CloudQuery's aws source plugin must exist in the hub, and be over version v1.0.0 | ||
func TestPluginLatestVersionWarns(t *testing.T) { | ||
versionWarner, err := NewPluginVersionWarner(zerolog.Nop(), "") | ||
require.NoError(t, err) | ||
hasWarned, err := versionWarner.WarnIfOutdated(context.Background(), "cloudquery", "aws", "source", "v1.0.0") | ||
assert.NoError(t, err) | ||
assert.True(t, hasWarned) | ||
} |