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  }