github.com/kubeshop/testkube@v1.17.23/pkg/imageinspector/inspector.go (about) 1 package imageinspector 2 3 import ( 4 "context" 5 "fmt" 6 "path/filepath" 7 8 "github.com/pkg/errors" 9 corev1 "k8s.io/api/core/v1" 10 11 "github.com/kubeshop/testkube/pkg/log" 12 ) 13 14 type inspector struct { 15 defaultRegistry string 16 fetcher InfoFetcher 17 secrets SecretFetcher 18 storage []Storage 19 } 20 21 func NewInspector(defaultRegistry string, infoFetcher InfoFetcher, secretFetcher SecretFetcher, storage ...Storage) Inspector { 22 return &inspector{ 23 defaultRegistry: defaultRegistry, 24 fetcher: infoFetcher, 25 secrets: secretFetcher, 26 storage: storage, 27 } 28 } 29 30 func (i *inspector) get(ctx context.Context, registry, image string) *Info { 31 for _, s := range i.storage { 32 v, err := s.Get(ctx, RequestBase{Registry: registry, Image: image}) 33 if err != nil && !errors.Is(err, context.Canceled) { 34 log.DefaultLogger.Warnw("error while getting image details from cache", "registry", registry, "image", image, "error", err) 35 } 36 if v != nil { 37 return v 38 } 39 } 40 return nil 41 } 42 43 func (i *inspector) fetch(ctx context.Context, registry, image string, pullSecretNames []string) (*Info, error) { 44 // Fetch the secrets 45 secrets := make([]corev1.Secret, len(pullSecretNames)) 46 for idx, name := range pullSecretNames { 47 secret, err := i.secrets.Get(ctx, name) 48 if err != nil { 49 return nil, errors.Wrap(err, fmt.Sprintf("fetching '%s' pull secret", name)) 50 } 51 secrets[idx] = *secret 52 } 53 54 // Load the image details 55 info, err := i.fetcher.Fetch(ctx, registry, image, secrets) 56 if err != nil { 57 return nil, errors.Wrap(err, fmt.Sprintf("fetching '%s' image from '%s' registry", image, registry)) 58 } else if info == nil { 59 return nil, fmt.Errorf("unknown problem with fetching '%s' image from '%s' registry", image, registry) 60 } 61 if info.Shell != "" && !filepath.IsAbs(info.Shell) { 62 info.Shell = "" 63 } 64 return info, err 65 } 66 67 func (i *inspector) save(ctx context.Context, registry, image string, info *Info) { 68 if info == nil { 69 return 70 } 71 for _, s := range i.storage { 72 if err := s.Store(ctx, RequestBase{Registry: registry, Image: image}, *info); err != nil { 73 log.DefaultLogger.Warnw("error while saving image details in the cache", "registry", registry, "image", image, "error", err) 74 } 75 } 76 } 77 78 func (i *inspector) Inspect(ctx context.Context, registry, image string, pullPolicy corev1.PullPolicy, pullSecretNames []string) (*Info, error) { 79 // Load from cache 80 if pullPolicy != corev1.PullAlways { 81 value := i.get(ctx, registry, image) 82 if value != nil { 83 return value, nil 84 } 85 } 86 87 // Fetch the data 88 value, err := i.fetch(ctx, registry, image, pullSecretNames) 89 if err != nil { 90 return nil, errors.Wrap(err, fmt.Sprintf("inspecting image: '%s' at '%s' registry", image, registry)) 91 } 92 if value == nil { 93 return nil, fmt.Errorf("not found image details for: '%s' at '%s' registry", image, registry) 94 } 95 96 // Save asynchronously 97 go i.save(context.Background(), registry, image, value) 98 99 return value, nil 100 }