github.com/jfrog/jfrog-cli-core/v2@v2.52.0/artifactory/utils/container/remoteagent.go (about)

     1  package container
     2  
     3  import (
     4  	"strings"
     5  
     6  	buildinfo "github.com/jfrog/build-info-go/entities"
     7  	"github.com/jfrog/jfrog-client-go/artifactory"
     8  	"github.com/jfrog/jfrog-client-go/artifactory/services/utils"
     9  	"github.com/jfrog/jfrog-client-go/utils/errorutils"
    10  	"github.com/jfrog/jfrog-client-go/utils/log"
    11  )
    12  
    13  // Build-info builder for remote agents tools such as: Kaniko, OpenShift CLI (oc), or buildx.
    14  type RemoteAgentBuildInfoBuilder struct {
    15  	buildInfoBuilder *buildInfoBuilder
    16  	manifestSha2     string
    17  }
    18  
    19  func NewRemoteAgentBuildInfoBuilder(image *Image, repository, buildName, buildNumber, project string, serviceManager artifactory.ArtifactoryServicesManager, manifestSha256 string) (*RemoteAgentBuildInfoBuilder, error) {
    20  	builder, err := newBuildInfoBuilder(image, repository, buildName, buildNumber, project, serviceManager)
    21  	return &RemoteAgentBuildInfoBuilder{
    22  		buildInfoBuilder: builder,
    23  		manifestSha2:     manifestSha256,
    24  	}, err
    25  }
    26  
    27  func (rabib *RemoteAgentBuildInfoBuilder) GetLayers() *[]utils.ResultItem {
    28  	return &rabib.buildInfoBuilder.imageLayers
    29  }
    30  
    31  func (rabib *RemoteAgentBuildInfoBuilder) Build(module string) (*buildinfo.BuildInfo, error) {
    32  	// Search for and image in Artifactory.
    33  	results, err := rabib.searchImage()
    34  	if err != nil {
    35  		return nil, err
    36  	}
    37  	// Create build-info based on image manifest.
    38  	if results["manifest.json"] != nil {
    39  		searchResults, manifest, err := rabib.handleManifest(results)
    40  		if err != nil {
    41  			return nil, err
    42  		}
    43  		return rabib.buildInfoBuilder.createBuildInfo(Push, manifest, searchResults, module)
    44  	}
    45  	// Create build-info based on image fat-manifest.
    46  	multiPlatformImages, fatManifestDetails, fatManifest, err := rabib.handleFatManifestImage(results)
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  	return rabib.buildInfoBuilder.createMultiPlatformBuildInfo(fatManifest, fatManifestDetails, multiPlatformImages, module)
    51  }
    52  
    53  // Search for image manifest and layers in Artifactory.
    54  func (rabib *RemoteAgentBuildInfoBuilder) handleManifest(resultMap map[string]*utils.ResultItem) (map[string]*utils.ResultItem, *manifest, error) {
    55  	if manifest, ok := resultMap["manifest.json"]; ok {
    56  		err := rabib.isVerifiedManifest(manifest)
    57  		if err != nil {
    58  			return nil, nil, err
    59  
    60  		}
    61  		manifest, err := getManifest(resultMap, rabib.buildInfoBuilder.serviceManager, rabib.buildInfoBuilder.repositoryDetails.key)
    62  		if err != nil {
    63  			return nil, nil, err
    64  		}
    65  		// // Manifest may hold 'empty layers'. As a result, promotion will fail to promote the same layer more than once.
    66  		rabib.buildInfoBuilder.imageSha2 = manifest.Config.Digest
    67  		log.Debug("Found manifest.json. Proceeding to create build-info.")
    68  		return resultMap, manifest, nil
    69  	}
    70  	return nil, nil, errorutils.CheckErrorf(`couldn't find image "` + rabib.buildInfoBuilder.image.name + `" manifest in Artifactory`)
    71  }
    72  
    73  func (rabib *RemoteAgentBuildInfoBuilder) handleFatManifestImage(results map[string]*utils.ResultItem) (map[string][]*utils.ResultItem, *utils.ResultItem, *FatManifest, error) {
    74  	if fatManifestResult, ok := results["list.manifest.json"]; ok {
    75  		log.Debug("Found list.manifest.json. Proceeding to create build-info.")
    76  		fatManifestRootPath := getFatManifestRoot(fatManifestResult.GetItemRelativeLocation()) + "/*"
    77  		fatManifest, err := getFatManifest(results, rabib.buildInfoBuilder.serviceManager, rabib.buildInfoBuilder.repositoryDetails.key)
    78  		if err != nil {
    79  			return nil, nil, nil, err
    80  		}
    81  		multiPlatformImages, err := performMultiPlatformImageSearch(fatManifestRootPath, rabib.buildInfoBuilder.serviceManager)
    82  		return multiPlatformImages, fatManifestResult, fatManifest, err
    83  	}
    84  	return nil, nil, nil, errorutils.CheckErrorf(`couldn't find image "` + rabib.buildInfoBuilder.image.name + `" fat manifest in Artifactory`)
    85  }
    86  
    87  // Search image manifest or fat-manifest of and image.
    88  func (rabib *RemoteAgentBuildInfoBuilder) searchImage() (resultMap map[string]*utils.ResultItem, err error) {
    89  	longImageName, err := rabib.buildInfoBuilder.image.GetImageLongNameWithTag()
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  	imagePath := strings.Replace(longImageName, ":", "/", 1)
    94  
    95  	// Search image's manifest.
    96  	manifestPathsCandidates := getManifestPaths(imagePath, rabib.buildInfoBuilder.getSearchableRepo(), Push)
    97  	log.Debug("Start searching for image manifest.json")
    98  	for _, path := range manifestPathsCandidates {
    99  		log.Debug(`Searching in:"` + path + `"`)
   100  		resultMap, err = performSearch(path, rabib.buildInfoBuilder.serviceManager)
   101  		if err != nil {
   102  			return nil, err
   103  		}
   104  		if resultMap == nil {
   105  			continue
   106  		}
   107  		if resultMap["list.manifest.json"] != nil || resultMap["manifest.json"] != nil {
   108  			return resultMap, nil
   109  		}
   110  	}
   111  	return nil, errorutils.CheckErrorf(imageNotFoundErrorMessage, rabib.buildInfoBuilder.image.name)
   112  }
   113  
   114  // Verify manifest's sha256. If there is no match, return nil.
   115  func (rabib *RemoteAgentBuildInfoBuilder) isVerifiedManifest(imageManifest *utils.ResultItem) error {
   116  	if imageManifest.GetProperty("docker.manifest.digest") != rabib.manifestSha2 {
   117  		return errorutils.CheckErrorf(`Found incorrect manifest.json file. Expects digest "` + rabib.manifestSha2 + `" found "` + imageManifest.GetProperty("docker.manifest.digest"))
   118  	}
   119  	return nil
   120  }
   121  
   122  func getFatManifestRoot(fatManifestPath string) string {
   123  	fatManifestPath = strings.TrimSuffix(fatManifestPath, "/")
   124  	return fatManifestPath[:strings.LastIndex(fatManifestPath, "/")]
   125  }