Skip to content

Commit

Permalink
Re-implement the setup-envtest functionality in a package
Browse files Browse the repository at this point in the history
There are two main points of re-implementing vs just moving the code:

1. Error handling in the old code base was based on panicking and recovering,
   where the recover basically just read out a field from the panic value and
   determined the correct exit code.

   In a package, we want more nuanced error handling, especially in order to
   allow test suites to catch the errors and surface them through their own
   reporting mechanisms.

2. There was a lot of global state in the old code base, "hidden" in the
   env.Env type that was used as a receiver for all the methods.

   This re-implementation tries to make the state more explicit, keeping only
   dependencies (like the remote client and local store) in the environment,
   while retaining the same behavior as the previous implementation.

Tests have been ported over to their respective workflow sub-packages, and
a few new ones have been added to cover cases the old test suite for one reason
or another did not. Thus, we can be fairly confident that the new implementation
does not break old functionality, even if it is a significant rewrite.
  • Loading branch information
tomasaschan committed Jun 14, 2024
1 parent 1f5b39f commit c8aa9b3
Show file tree
Hide file tree
Showing 55 changed files with 2,389 additions and 1,985 deletions.
4 changes: 2 additions & 2 deletions examples/scratch-env/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ require (
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.3.1 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/imdario/mergo v0.3.6 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
Expand All @@ -46,7 +46,7 @@ require (
golang.org/x/sys v0.19.0 // indirect
golang.org/x/term v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/time v0.5.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.33.0 // indirect
Expand Down
8 changes: 4 additions & 4 deletions examples/scratch-env/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
Expand Down Expand Up @@ -137,8 +137,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
Expand Down
13 changes: 8 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ require (
sigs.k8s.io/yaml v1.3.0
)

require golang.org/x/mod v0.15.0
require (
github.com/spf13/afero v1.11.0
golang.org/x/mod v0.15.0
)

require (
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
Expand All @@ -53,7 +56,7 @@ require (
github.com/google/cel-go v0.20.1 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect
github.com/google/uuid v1.3.1 // indirect
github.com/google/uuid v1.4.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
github.com/imdario/mergo v0.3.6 // indirect
github.com/josharian/intern v1.0.0 // indirect
Expand Down Expand Up @@ -81,11 +84,11 @@ require (
golang.org/x/sync v0.6.0 // indirect
golang.org/x/term v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.18.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect
google.golang.org/grpc v1.59.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
Expand Down
22 changes: 12 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
Expand Down Expand Up @@ -110,6 +110,8 @@ github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
Expand Down Expand Up @@ -185,8 +187,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
Expand All @@ -201,12 +203,12 @@ gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw
gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY=
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4=
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:DoPTO70H+bcDXcd39vOqb2viZxgqeBeSGtZ55yZU4/Q=
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ=
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY=
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo=
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc=
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
Expand Down
41 changes: 41 additions & 0 deletions pkg/envtest/setup/cleanup/cleanup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package cleanup

import (
"context"
"errors"

"sigs.k8s.io/controller-runtime/pkg/envtest/setup/env"
"sigs.k8s.io/controller-runtime/pkg/envtest/setup/store"
"sigs.k8s.io/controller-runtime/pkg/envtest/setup/versions"
)

// Result is a list of version-platform pairs that were removed from the store.
type Result []store.Item

// Cleanup removes binary packages from disk for all version-platform pairs that match the parameters
//
// Note that both the item collection and the error might be non-nil, if some packages were successfully
// removed (they will be listed in the first return value) and some failed (the errors will be collected
// in the second).
func Cleanup(ctx context.Context, spec versions.Spec, options ...Option) (Result, error) {
cfg := configure(options...)

env, err := env.New(cfg.envOpts...)
if err != nil {
return nil, err
}

if err := env.Store.Initialize(ctx); err != nil {
return nil, err
}

items, err := env.Store.Remove(ctx, store.Filter{Version: spec, Platform: cfg.platform})
if errors.Is(err, store.ErrUnableToList) {
return nil, err
}

// store.Remove returns an error if _any_ item failed to be removed,
// but it also reports any items that were removed without errors.
// Therefore, both items and err might be non-nil at the same time.
return items, err
}
97 changes: 97 additions & 0 deletions pkg/envtest/setup/cleanup/cleanup_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package cleanup_test

import (
"context"
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/spf13/afero"

"github.com/go-logr/logr"
"sigs.k8s.io/controller-runtime/pkg/envtest/setup/cleanup"
"sigs.k8s.io/controller-runtime/pkg/envtest/setup/env"
"sigs.k8s.io/controller-runtime/pkg/envtest/setup/store"
"sigs.k8s.io/controller-runtime/pkg/envtest/setup/testhelpers"
"sigs.k8s.io/controller-runtime/pkg/envtest/setup/versions"
)

var (
testLog logr.Logger
ctx context.Context
)

func TestCleanup(t *testing.T) {
testLog = testhelpers.GetLogger()
ctx = logr.NewContext(context.Background(), testLog)

RegisterFailHandler(Fail)
RunSpecs(t, "Cleanup Suite")
}

var _ = Describe("Cleanup", func() {
var (
defaultEnvOpts []env.Option
s *store.Store
)

BeforeEach(func() {
s = testhelpers.NewMockStore()
})

JustBeforeEach(func() {
defaultEnvOpts = []env.Option{
env.WithClient(nil), // ensures we fail if we try to connect
env.WithStore(s),
env.WithFS(afero.NewIOFS(s.Root)),
}
})

Context("when cleanup is run", func() {
version := versions.Spec{
Selector: versions.Concrete{
Major: 1,
Minor: 16,
Patch: 1,
},
}

var (
matching, nonMatching []store.Item
)

BeforeEach(func() {
// ensure there are some versions matching what we're about to delete
var err error
matching, err = s.List(ctx, store.Filter{Version: version, Platform: versions.Platform{OS: "linux", Arch: "amd64"}})
Expect(err).NotTo(HaveOccurred())
Expect(matching).NotTo(BeEmpty(), "found no matching versions before cleanup")

// ensure there are some versions _not_ matching what we're about to delete
nonMatching, err = s.List(ctx, store.Filter{Version: versions.Spec{Selector: versions.PatchSelector{Major: 1, Minor: 17, Patch: versions.AnyPoint}}, Platform: versions.Platform{OS: "linux", Arch: "amd64"}})
Expect(err).NotTo(HaveOccurred())
Expect(nonMatching).NotTo(BeEmpty(), "found no non-matching versions before cleanup")
})

JustBeforeEach(func() {
Expect(cleanup.Cleanup(
ctx,
version,
cleanup.WithPlatform("linux", "amd64"),
cleanup.WithEnvOptions(defaultEnvOpts...),
)).To(Succeed())
})

It("should remove matching versions", func() {
items, err := s.List(ctx, store.Filter{Version: version, Platform: versions.Platform{OS: "linux", Arch: "amd64"}})
Expect(err).NotTo(HaveOccurred())
Expect(items).To(BeEmpty(), "found matching versions after cleanup")
})

It("should not remove non-matching versions", func() {
items, err := s.List(ctx, store.Filter{Version: versions.AnyVersion, Platform: versions.Platform{OS: "*", Arch: "*"}})
Expect(err).NotTo(HaveOccurred())
Expect(items).To(ContainElements(nonMatching), "non-matching items were affected")
})
})
})
45 changes: 45 additions & 0 deletions pkg/envtest/setup/cleanup/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package cleanup

import (
"runtime"

"sigs.k8s.io/controller-runtime/pkg/envtest/setup/env"
"sigs.k8s.io/controller-runtime/pkg/envtest/setup/versions"
)

type config struct {
envOpts []env.Option
platform versions.Platform
}

// Option is a functional option for configuring the cleanup process.
type Option func(*config)

// WithEnvOptions adds options to the environment setup.
func WithEnvOptions(opts ...env.Option) Option {
return func(cfg *config) {
cfg.envOpts = append(cfg.envOpts, opts...)
}
}

// WithPlatform sets the platform to use for cleanup.
func WithPlatform(os string, arch string) Option {
return func(cfg *config) {
cfg.platform = versions.Platform{OS: os, Arch: arch}
}
}

func configure(options ...Option) *config {
cfg := &config{
platform: versions.Platform{
Arch: runtime.GOARCH,
OS: runtime.GOOS,
},
}

for _, opt := range options {
opt(cfg)
}

return cfg
}
69 changes: 69 additions & 0 deletions pkg/envtest/setup/env/assets.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package env

import (
"context"
"errors"
"fmt"
"io/fs"
"path/filepath"

"sigs.k8s.io/controller-runtime/pkg/envtest/setup/versions"
"sigs.k8s.io/controller-runtime/pkg/log"
)

var expectedExecutables = []string{
"kube-apiserver",
"etcd",
"kubectl",
}

// TryUseAssetsFromPath attempts to use the assets from the provided path if they match the spec.
// If they do not, or if some executable is missing, it returns an empty string.
func (e *Env) TryUseAssetsFromPath(ctx context.Context, spec versions.Spec, path string) (versions.Spec, bool) {
v, err := versions.FromPath(path)
if err != nil {
ok, checkErr := e.hasAllExecutables(path)
log.FromContext(ctx).Info("has all executables", "ok", ok, "err", checkErr)
if checkErr != nil {
log.FromContext(ctx).Error(errors.Join(err, checkErr), "Failed checking if assets path has all binaries, ignoring", "path", path)
return versions.Spec{}, false
} else if ok {
// If the path has all executables, we can use it even if we can't parse the version.
// The user explicitly asked for this path, so set the version to a wildcard so that
// it passes checks downstream.
return versions.AnyVersion, true
}

log.FromContext(ctx).Error(errors.Join(err, errors.New("some required binaries missing")), "Unable to use assets from path, ignoring", "path", path)
return versions.Spec{}, false
}

if !spec.Matches(*v) {
log.FromContext(ctx).Error(nil, "Assets path does not match spec, ignoring", "path", path, "spec", spec)
return versions.Spec{}, false
}

if ok, err := e.hasAllExecutables(path); err != nil {
log.FromContext(ctx).Error(err, "Failed checking if assets path has all binaries, ignoring", "path", path)
return versions.Spec{}, false
} else if !ok {
log.FromContext(ctx).Error(nil, "Assets path is missing some executables, ignoring", "path", path)
return versions.Spec{}, false
}

return versions.Spec{Selector: v}, true
}

func (e *Env) hasAllExecutables(path string) (bool, error) {
for _, expected := range expectedExecutables {
_, err := e.FS.Open(filepath.Join(path, expected))
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
return false, nil
}
return false, fmt.Errorf("check for existence of %s binary in %s: %w", expected, path, err)
}
}

return true, nil
}
Loading

0 comments on commit c8aa9b3

Please sign in to comment.