github.com/quay/claircore@v1.5.28/rhel/internal/containerapi/containerapi.go (about)

     1  // Package containerapi is a minimal client around the Red Hat Container API.
     2  package containerapi
     3  
     4  import (
     5  	"context"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io"
     9  	"net/http"
    10  	"net/url"
    11  	"path"
    12  	"strings"
    13  
    14  	"github.com/quay/zlog"
    15  )
    16  
    17  type containerImages struct {
    18  	Images []containerImage `json:"data"`
    19  }
    20  type containerImage struct {
    21  	CPEs       []string   `json:"cpe_ids"`
    22  	ParsedData parsedData `json:"parsed_data"`
    23  }
    24  type parsedData struct {
    25  	Architecture string  `json:"architecture"`
    26  	Labels       []label `json:"labels"`
    27  }
    28  type label struct {
    29  	Name  string `json:"name"`
    30  	Value string `json:"value"`
    31  }
    32  
    33  // ContainerAPI gets container metadata from Red Hat's API.
    34  type ContainerAPI struct {
    35  	Client *http.Client
    36  	Root   *url.URL
    37  }
    38  
    39  // GetCPEs fetches CPE information for given build from Red Hat Container API.
    40  func (c *ContainerAPI) GetCPEs(ctx context.Context, nvr, arch string) ([]string, error) {
    41  	uri, err := c.Root.Parse(path.Join("v1/images/nvr/", nvr))
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  	req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri.String(), nil)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  
    50  	zlog.Debug(ctx).
    51  		Str("uri", uri.String()).
    52  		Msg("making container API request")
    53  	res, err := c.Client.Do(req)
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  	defer res.Body.Close()
    58  	if res.StatusCode != http.StatusOK {
    59  		var b strings.Builder
    60  		if _, err := io.Copy(&b, res.Body); err != nil {
    61  			zlog.Warn(ctx).Err(err).Msg("additional error while reading response")
    62  		} else {
    63  			zlog.Warn(ctx).Str("response", b.String()).Msg("received error response")
    64  		}
    65  		return nil, fmt.Errorf("rhel: unexpected response: %d %s", res.StatusCode, res.Status)
    66  	}
    67  
    68  	var ci containerImages
    69  	if err := json.NewDecoder(res.Body).Decode(&ci); err != nil {
    70  		return nil, err
    71  	}
    72  	for _, image := range ci.Images {
    73  		for _, label := range image.ParsedData.Labels {
    74  			if label.Name == "architecture" {
    75  				if label.Value == arch {
    76  					return image.CPEs, nil
    77  				}
    78  			}
    79  		}
    80  	}
    81  	return nil, nil
    82  }