-
Notifications
You must be signed in to change notification settings - Fork 94
Expand file tree
/
Copy pathremoteagent.go
More file actions
149 lines (134 loc) · 6.35 KB
/
remoteagent.go
File metadata and controls
149 lines (134 loc) · 6.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package container
import (
"strings"
buildinfo "github.com/jfrog/build-info-go/entities"
"github.com/jfrog/jfrog-client-go/artifactory"
"github.com/jfrog/jfrog-client-go/artifactory/services/utils"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
"github.com/jfrog/jfrog-client-go/utils/log"
)
// Build-info builder for remote agents tools such as: Kaniko, OpenShift CLI (oc), or buildx.
type RemoteAgentBuildInfoBuilder struct {
buildInfoBuilder *buildInfoBuilder
manifestSha2 string
}
func NewRemoteAgentBuildInfoBuilder(image *Image, repository, buildName, buildNumber, project string, serviceManager artifactory.ArtifactoryServicesManager, manifestSha256 string) (*RemoteAgentBuildInfoBuilder, error) {
builder, err := newBuildInfoBuilder(image, repository, buildName, buildNumber, project, serviceManager)
return &RemoteAgentBuildInfoBuilder{
buildInfoBuilder: builder,
manifestSha2: manifestSha256,
}, err
}
func (rabib *RemoteAgentBuildInfoBuilder) GetLayers() *[]utils.ResultItem {
return &rabib.buildInfoBuilder.imageLayers
}
func (rabib *RemoteAgentBuildInfoBuilder) Build(module string) (*buildinfo.BuildInfo, error) {
// Search for and image in Artifactory.
results, err := rabib.searchImage()
if err != nil {
return nil, err
}
// Create build-info based on image manifest.
if results["manifest.json"] != nil {
searchResults, manifest, err := rabib.handleManifest(results)
if err != nil {
return nil, err
}
return rabib.buildInfoBuilder.createBuildInfo(Push, manifest, searchResults, module)
}
// Create build-info based on image fat-manifest.
multiPlatformImages, fatManifestDetails, fatManifest, err := rabib.handleFatManifestImage(results)
if err != nil {
return nil, err
}
return rabib.buildInfoBuilder.createMultiPlatformBuildInfo(fatManifest, fatManifestDetails, multiPlatformImages, module)
}
// Search for image manifest and layers in Artifactory.
func (rabib *RemoteAgentBuildInfoBuilder) handleManifest(resultMap map[string]*utils.ResultItem) (map[string]*utils.ResultItem, *manifest, error) {
if manifest, ok := resultMap["manifest.json"]; ok {
if !rabib.isVerifiedManifest(manifest) {
log.Debug("Manifest verification failed, continuing with SHA-based validation...")
}
manifest, err := getManifest(resultMap, rabib.buildInfoBuilder.serviceManager, rabib.buildInfoBuilder.repositoryDetails.key)
if err != nil {
return nil, nil, err
}
// Manifest may hold 'empty layers'. As a result, promotion will fail to promote the same layer more than once.
rabib.buildInfoBuilder.imageSha2 = manifest.Config.Digest
log.Debug("Found manifest.json. Proceeding to create build-info.")
return resultMap, manifest, nil
}
return nil, nil, errorutils.CheckErrorf(`couldn't find image "%s" manifest in Artifactory`, rabib.buildInfoBuilder.image.name)
}
func (rabib *RemoteAgentBuildInfoBuilder) handleFatManifestImage(results map[string]*utils.ResultItem) (map[string][]*utils.ResultItem, *utils.ResultItem, *FatManifest, error) {
if fatManifestResult, ok := results["list.manifest.json"]; ok {
log.Debug("Found list.manifest.json. Proceeding to create build-info.")
fatManifestRootPath := getFatManifestRoot(fatManifestResult.GetItemRelativeLocation()) + "/*"
fatManifest, err := getFatManifest(results, rabib.buildInfoBuilder.serviceManager, rabib.buildInfoBuilder.repositoryDetails.key)
if err != nil {
return nil, nil, nil, err
}
multiPlatformImages, err := performMultiPlatformImageSearch(fatManifestRootPath, rabib.buildInfoBuilder.serviceManager)
return multiPlatformImages, fatManifestResult, fatManifest, err
}
return nil, nil, nil, errorutils.CheckErrorf(`couldn't find image "%s" fat manifest in Artifactory`, rabib.buildInfoBuilder.image.name)
}
// Search image manifest or fat-manifest of and image.
func (rabib *RemoteAgentBuildInfoBuilder) searchImage() (resultMap map[string]*utils.ResultItem, err error) {
longImageName, err := rabib.buildInfoBuilder.image.GetImageLongNameWithTag()
if err != nil {
return nil, err
}
imagePath := strings.Replace(longImageName, ":", "/", 1)
// Search image's manifest.
manifestPathsCandidates := getManifestPaths(imagePath, rabib.buildInfoBuilder.getSearchableRepo(), Push)
log.Debug("Start searching for image manifest.json")
// First try standard tag-based search
for _, path := range manifestPathsCandidates {
log.Debug(`Searching in:"` + path + `"`)
resultMap, err = performSearch(path, rabib.buildInfoBuilder.serviceManager)
if err != nil {
return nil, err
}
if resultMap == nil {
continue
}
if resultMap["list.manifest.json"] != nil || resultMap["manifest.json"] != nil {
return resultMap, nil
}
}
// If tag-based search failed and we have a SHA, try SHA-based search
if rabib.manifestSha2 != "" {
log.Debug("Tag-based search failed. Trying SHA-based search with: " + rabib.manifestSha2)
// Extract repository path without tag
repoPath := imagePath[:strings.LastIndex(imagePath, "/")]
// Convert SHA format from sha256:xxx to sha256__xxx for Artifactory path format
shaPath := strings.Replace(rabib.manifestSha2, ":", "__", 1)
// Search for the image using SHA path
shaSearchPath := repoPath + "/" + shaPath + "/*"
log.Debug(`Searching by SHA in:"` + shaSearchPath + `"`)
resultMap, err = performSearch(shaSearchPath, rabib.buildInfoBuilder.serviceManager)
if err != nil {
return nil, err
}
if resultMap != nil && (resultMap["list.manifest.json"] != nil || resultMap["manifest.json"] != nil) {
log.Info("Found image by SHA digest in repository")
return resultMap, nil
}
}
return nil, errorutils.CheckErrorf(imageNotFoundErrorMessage, rabib.buildInfoBuilder.image.name)
}
// Verify manifest's sha256. Returns true if manifest is verified, false otherwise.
func (rabib *RemoteAgentBuildInfoBuilder) isVerifiedManifest(imageManifest *utils.ResultItem) bool {
if imageManifest.GetProperty("docker.manifest.digest") != rabib.manifestSha2 {
manifestDigest := imageManifest.GetProperty("docker.manifest.digest")
log.Warn("Manifest digest mismatch detected. Local image digest: " + rabib.manifestSha2 + ", Repository digest: " + manifestDigest)
log.Info("Proceeding with SHA-based validation to ensure correct image identification...")
return false
}
return true
}
func getFatManifestRoot(fatManifestPath string) string {
fatManifestPath = strings.TrimSuffix(fatManifestPath, "/")
return fatManifestPath[:strings.LastIndex(fatManifestPath, "/")]
}