From 84fd4970343d3529065a7f8ae0681d0b087c7d1a Mon Sep 17 00:00:00 2001 From: michaeljguarino Date: Fri, 14 Jul 2023 03:11:10 -0400 Subject: [PATCH] Implement cli version checking (#423) * Implement cli version checking We had introduced the ability to specify min cli versions in the api, but never enforced. This should implement an enforcement layer for them. * implement unit tests --- Makefile | 8 +++-- cmd/plural/deploy.go | 5 ++++ cmd/plural/version.go | 10 +++++++ go.mod | 4 +-- go.sum | 10 +++---- pkg/api/charts.go | 3 ++ pkg/api/models.go | 1 + pkg/wkspace/builder.go | 23 +++++++++++++++ pkg/wkspace/builder_test.go | 59 +++++++++++++++++++++++++++++++++++++ 9 files changed, 113 insertions(+), 10 deletions(-) create mode 100644 pkg/wkspace/builder_test.go diff --git a/Makefile b/Makefile index a269c4c54..ced6101dd 100644 --- a/Makefile +++ b/Makefile @@ -141,9 +141,13 @@ release-vsn: # tags and pushes a new release git tag -a $$tag -m "new release"; \ git push origin $$tag +.PHONY: setup-tests +setup-tests: + go install gotest.tools/gotestsum@latest + .PHONY: test -test: - go test -v -race ./pkg/... ./cmd/... +test: setup-tests + gotestsum --format testname -- -v -race ./pkg/... ./cmd/... .PHONY: format format: # formats all go code to prep for linting diff --git a/cmd/plural/deploy.go b/cmd/plural/deploy.go index 91d49060d..537e3d1cd 100644 --- a/cmd/plural/deploy.go +++ b/cmd/plural/deploy.go @@ -131,6 +131,11 @@ func (p *Plural) doBuild(installation *api.Installation, force bool) error { return err } + vsn, ok := workspace.RequiredCliVsn() + if ok && !versionValid(vsn) { + return fmt.Errorf("Your cli version is not sufficient to complete this build, please update to at least %s", vsn) + } + if err := workspace.Prepare(); err != nil { return err } diff --git a/cmd/plural/version.go b/cmd/plural/version.go index 07466d0cf..410f04c3d 100644 --- a/cmd/plural/version.go +++ b/cmd/plural/version.go @@ -6,6 +6,8 @@ import ( "runtime" "strings" + "golang.org/x/mod/semver" + "github.com/pluralsh/plural/pkg/utils" "github.com/urfave/cli" ) @@ -20,6 +22,14 @@ var ( Date = "" ) +func versionValid(vsn string) bool { + current := Version + if !strings.HasPrefix(current, "v") { + current = fmt.Sprintf("v%s", current) + } + return semver.Compare(vsn, current) <= 0 +} + func checkRecency() error { if os.Getenv("CLOUD_SHELL") == "1" || os.Getenv("PLURAL_CONSOLE") == "1" { return nil diff --git a/go.mod b/go.mod index f582583eb..0af4d2089 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 github.com/Azure/go-autorest/autorest/to v0.4.0 github.com/Masterminds/sprig/v3 v3.2.3 - github.com/Yamashou/gqlgenc v0.11.0 + github.com/Yamashou/gqlgenc v0.14.0 github.com/aws/aws-sdk-go-v2 v1.17.4 github.com/aws/aws-sdk-go-v2/service/iam v1.19.2 github.com/aws/aws-sdk-go-v2/service/sts v1.16.17 @@ -45,7 +45,7 @@ require ( github.com/olekukonko/tablewriter v0.0.5 github.com/packethost/packngo v0.29.0 github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 - github.com/pluralsh/gqlclient v1.3.18 + github.com/pluralsh/gqlclient v1.6.0 github.com/pluralsh/plural-operator v0.5.3 github.com/pluralsh/polly v0.1.1 github.com/rodaine/hclencoder v0.0.1 diff --git a/go.sum b/go.sum index 372d712d2..54af77e64 100644 --- a/go.sum +++ b/go.sum @@ -148,8 +148,8 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= -github.com/Yamashou/gqlgenc v0.11.0 h1:y6I7CDrUdY4JBxfwss9168HTP5k/CdExLV5+YPG/3nY= -github.com/Yamashou/gqlgenc v0.11.0/go.mod h1:OeQhghEgvGWvRwzx9XjMeg3FUQOHnTo5/12iuJSJxLg= +github.com/Yamashou/gqlgenc v0.14.0 h1:KVzUuVQKfl4Phm5Cw4yeFThDAxZoIBR9XLoK/4O1O6U= +github.com/Yamashou/gqlgenc v0.14.0/go.mod h1:+z+FRCtGrNmgTxweAUiCodOmQJLTCNtnRNAqhewf1Q8= github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= @@ -922,10 +922,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pluralsh/controller-reconcile-helper v0.0.4 h1:1o+7qYSyoeqKFjx+WgQTxDz4Q2VMpzprJIIKShxqG0E= github.com/pluralsh/controller-reconcile-helper v0.0.4/go.mod h1:AfY0gtteD6veBjmB6jiRx/aR4yevEf6K0M13/pGan/s= -github.com/pluralsh/gqlclient v1.3.17 h1:hD/rG+lhxP3kN1UUXrzZd2uN7P76MvNTEJEzYOpERXo= -github.com/pluralsh/gqlclient v1.3.17/go.mod h1:z1qHnvPeqIN/a+5OzFs40e6HI6tDxzh1+yJuEpvqGy4= -github.com/pluralsh/gqlclient v1.3.18 h1:SthOBnlEgXh1bAKQXrZDNZRekaw3zDku4I4xgVsumDE= -github.com/pluralsh/gqlclient v1.3.18/go.mod h1:z1qHnvPeqIN/a+5OzFs40e6HI6tDxzh1+yJuEpvqGy4= +github.com/pluralsh/gqlclient v1.6.0 h1:7R0H98XrZdBdl8rQQGVGKkCY9iMStyrX+0lZ3zuArqo= +github.com/pluralsh/gqlclient v1.6.0/go.mod h1:qSXKUlio1F2DRPy8el4oFYsmpKbkUYspgPB87T4it5I= github.com/pluralsh/oauth v0.9.2 h1:tM9hBK4tCnJUeCOgX0ctxBBCS3hiCDPoxkJLODtedmQ= github.com/pluralsh/oauth v0.9.2/go.mod h1:aTUw/75rzcsbvW+/TLvWtHVDXFIdtFrDtUncOq9vHyM= github.com/pluralsh/plural-operator v0.5.3 h1:GaPL3LgimfzKZNHt7zXzqYZpb0hgyW9noHYnkA+rqNs= diff --git a/pkg/api/charts.go b/pkg/api/charts.go index 54fb3f7eb..3277717da 100644 --- a/pkg/api/charts.go +++ b/pkg/api/charts.go @@ -185,6 +185,9 @@ func convertDependencies(depFragment *gqlclient.DependenciesFragment) *Dependenc if depFragment.ProviderVsn != nil { dep.ProviderVsn = *depFragment.ProviderVsn } + if depFragment.CliVsn != nil { + dep.CliVsn = *depFragment.CliVsn + } if depFragment.Application != nil { dep.Application = *depFragment.Application } diff --git a/pkg/api/models.go b/pkg/api/models.go index 87c907fa2..17ea4fec6 100644 --- a/pkg/api/models.go +++ b/pkg/api/models.go @@ -76,6 +76,7 @@ type Dependencies struct { ProviderWirings map[string]interface{} Outputs map[string]interface{} ProviderVsn string + CliVsn string } type Dependency struct { diff --git a/pkg/wkspace/builder.go b/pkg/wkspace/builder.go index 5cb48eea1..bb34611cc 100644 --- a/pkg/wkspace/builder.go +++ b/pkg/wkspace/builder.go @@ -6,6 +6,8 @@ import ( "path/filepath" "strings" + "golang.org/x/mod/semver" + "github.com/pluralsh/plural/pkg/api" "github.com/pluralsh/plural/pkg/config" "github.com/pluralsh/plural/pkg/crypto" @@ -16,6 +18,8 @@ import ( "github.com/pluralsh/plural/pkg/utils" "github.com/pluralsh/plural/pkg/utils/git" "github.com/pluralsh/plural/pkg/utils/pathing" + + "github.com/pluralsh/polly/algorithms" ) type Workspace struct { @@ -92,6 +96,25 @@ func (wk *Workspace) PrintLinks() { doPrintLinks("terraform", wk.Links.Terraform) } +func (wk *Workspace) RequiredCliVsn() (vsn string, ok bool) { + cVsns := algorithms.Map(wk.Charts, func(c *api.ChartInstallation) string { return c.Version.Dependencies.CliVsn }) + tVsns := algorithms.Map(wk.Terraform, func(t *api.TerraformInstallation) string { return t.Version.Dependencies.CliVsn }) + vsns := algorithms.Filter(append(cVsns, tVsns...), func(v string) bool { return v != "" }) + vsns = algorithms.Map(vsns, func(v string) string { + if strings.HasPrefix(v, "v") { + return v + } + return fmt.Sprintf("v%s", v) + }) + vsns = algorithms.Filter(vsns, semver.IsValid) + if len(vsns) == 0 { + return + } + + semver.Sort(vsns) + return vsns[len(vsns)-1], true +} + func doPrintLinks(name string, links map[string]string) { if len(links) == 0 { return diff --git a/pkg/wkspace/builder_test.go b/pkg/wkspace/builder_test.go new file mode 100644 index 000000000..73caa3033 --- /dev/null +++ b/pkg/wkspace/builder_test.go @@ -0,0 +1,59 @@ +package wkspace + +import ( + "testing" + + "github.com/pluralsh/plural/pkg/api" + "github.com/stretchr/testify/assert" +) + +func TestRequiredCliVersion(t *testing.T) { + w := &Workspace{ + Charts: []*api.ChartInstallation{ + { + Version: &api.Version{ + Dependencies: &api.Dependencies{ + CliVsn: "0.1.0", + }, + }, + }, + }, + Terraform: []*api.TerraformInstallation{ + { + Version: &api.Version{ + Dependencies: &api.Dependencies{ + CliVsn: "0.2.0", + }, + }, + }, + }, + } + vsn, ok := w.RequiredCliVsn() + assert.True(t, ok) + assert.Equal(t, "v0.2.0", vsn) +} + +func TestRequiredCliVersionEmpty(t *testing.T) { + w := &Workspace{ + Charts: []*api.ChartInstallation{ + { + Version: &api.Version{ + Dependencies: &api.Dependencies{ + CliVsn: "bogus", + }, + }, + }, + }, + Terraform: []*api.TerraformInstallation{ + { + Version: &api.Version{ + Dependencies: &api.Dependencies{ + CliVsn: "", + }, + }, + }, + }, + } + _, ok := w.RequiredCliVsn() + assert.False(t, ok) +}