github.com/castai/kvisor@v1.7.1-0.20240516114728-b3572a2607b5/cmd/controller/state/imagescan/errors.go (about)

     1  package imagescan
     2  
     3  import (
     4  	"errors"
     5  	"regexp"
     6  	"strings"
     7  )
     8  
     9  const (
    10  	logrusErrorPattern = `time="(?P<Timestamp>.*?)" level=(?P<Level>.*?) msg="(?P<Message>.*?)" component=(?P<Component>.*)`
    11  )
    12  
    13  var (
    14  	re = regexp.MustCompile(logrusErrorPattern)
    15  
    16  	errImageScanLayerNotFound = errors.New("image layer not found")
    17  	errPrivateImage           = errors.New("private image")
    18  )
    19  
    20  type Log struct {
    21  	Timestamp string
    22  	Level     string
    23  	Message   string
    24  	Component string
    25  }
    26  
    27  func isPrivateImageError(rawErr error) bool {
    28  	errStr := strings.ToLower(rawErr.Error())
    29  
    30  	// Error codes from https://github.com/google/go-containerregistry/blob/190ad0e4d556f199a07951d55124f8a394ebccd9/pkg/v1/remote/transport/error.go#L115
    31  	// Connection refused error can happen for localhost image.
    32  	for _, errPart := range []string{"unauthorized", "manifest_unknown", "denied", "connection refused"} {
    33  		if strings.Contains(errStr, errPart) {
    34  			return true
    35  		}
    36  	}
    37  	return false
    38  }
    39  
    40  func isHostFSError(rawErr error) bool {
    41  	return strings.Contains(rawErr.Error(), "no such file or directory") || strings.Contains(rawErr.Error(), "failed to get the layer")
    42  }
    43  
    44  func parseErrorFromLog(rawErr error) error {
    45  	if isPrivateImageError(rawErr) {
    46  		return errPrivateImage
    47  	}
    48  	if isHostFSError(rawErr) {
    49  		return errImageScanLayerNotFound
    50  	}
    51  	logs := parseLogrusLog(rawErr.Error())
    52  	var errs []error
    53  	for _, log := range logs {
    54  		if log.Level == "error" || log.Level == "fatal" {
    55  			errs = append(errs, errors.New(log.Message))
    56  		}
    57  	}
    58  	if len(errs) != 0 {
    59  		return errors.Join(errs...)
    60  	}
    61  	return rawErr
    62  }
    63  
    64  func parseLogrusLog(logMessage string) []Log {
    65  	var logs []Log
    66  	lines := strings.Split(logMessage, "\n")
    67  	for _, line := range lines {
    68  		match := re.FindStringSubmatch(line)
    69  		if match != nil {
    70  			result := make(map[string]string)
    71  			for i, name := range re.SubexpNames() {
    72  				if i != 0 && name != "" {
    73  					result[name] = match[i]
    74  				}
    75  			}
    76  			logs = append(logs, Log{
    77  				Timestamp: result["Timestamp"],
    78  				Level:     result["Level"],
    79  				Message:   result["Message"],
    80  				Component: result["Component"],
    81  			})
    82  		}
    83  	}
    84  	return logs
    85  }