88 "io"
99 "log/slog"
1010 "net/http"
11+ "net/url"
1112 "slices"
1213 "strings"
1314 "time"
@@ -116,7 +117,12 @@ func (h *Handlers) handleCommonCompose(ctx echo.Context, composeRequest ComposeR
116117 repositories = append (repositories , snapshotRepos ... )
117118
118119 // A sanity check to make sure there's a snapshot for each repo
119- expected := len (buildRepositories (arch , composeRequest .ImageRequests [0 ].ImageType ))
120+ expected := 0
121+ for _ , r := range arch .Repositories {
122+ if len (r .ImageTypeTags ) == 0 || slices .Contains (r .ImageTypeTags , string (composeRequest .ImageRequests [0 ].ImageType )) {
123+ expected ++
124+ }
125+ }
120126 if len (repositories ) != expected {
121127 return ComposeResponse {}, fmt .Errorf ("no snapshots found for all repositories (found %d, expected %d)" , len (repositories ), expected )
122128 }
@@ -126,7 +132,10 @@ func (h *Handlers) handleCommonCompose(ctx echo.Context, composeRequest ComposeR
126132 return ComposeResponse {}, err
127133 }
128134 } else {
129- repositories = buildRepositories (arch , composeRequest .ImageRequests [0 ].ImageType )
135+ repositories , err = h .buildRepositoriesWithLatestSnapshots (ctx , arch , composeRequest .ImageRequests [0 ].ImageType )
136+ if err != nil {
137+ return ComposeResponse {}, err
138+ }
130139 }
131140
132141 uploadOptions , imageType , err := h .buildUploadOptions (ctx , composeRequest .ImageRequests [0 ].UploadRequest , composeRequest .ImageRequests [0 ].ImageType )
@@ -218,21 +227,108 @@ func (h *Handlers) handleCommonCompose(ctx echo.Context, composeRequest ComposeR
218227 }, nil
219228}
220229
221- func buildRepositories (arch * distribution.Architecture , imageType ImageTypes ) []composer.Repository {
230+ // buildRepositoriesWithLatestSnapshots transforms the CDN repository URLs to the latest snapshot URL
231+ // or falls back to CDN if snapshot is not available
232+ func (h * Handlers ) buildRepositoriesWithLatestSnapshots (ctx echo.Context , arch * distribution.Architecture , imageType ImageTypes ) ([]composer.Repository , error ) {
222233 var repositories []composer.Repository
234+ var cdnRepoURLs []string
235+ var errs []error
236+
223237 for _ , r := range arch .Repositories {
224- // If no image type tags are defined for the repo, add the repo
225- if len (r .ImageTypeTags ) == 0 || slices .Contains (r .ImageTypeTags , string (imageType )) {
226- repositories = append (repositories , composer.Repository {
227- Baseurl : r .Baseurl ,
228- Metalink : r .Metalink ,
229- Rhsm : common .ToPtr (r .Rhsm ),
230- Gpgkey : r .GpgKey ,
231- CheckGpg : r .CheckGpg ,
232- })
238+ if (len (r .ImageTypeTags ) == 0 || slices .Contains (r .ImageTypeTags , string (imageType ))) && r .Rhsm {
239+ cdnRepoURLs = append (cdnRepoURLs , * r .Baseurl )
240+ }
241+ }
242+
243+ var repoMap map [string ]content_sources.ApiRepositoryResponse
244+ if len (cdnRepoURLs ) > 0 {
245+ var err error
246+ repoMap , err = h .server .csClient .GetRepositories (ctx .Request ().Context (), cdnRepoURLs , nil , false )
247+ if err != nil {
248+ return nil , fmt .Errorf ("unable to retrieve get repositories: %w" , err )
233249 }
234250 }
235- return repositories
251+
252+ for _ , r := range arch .Repositories {
253+ if len (r .ImageTypeTags ) > 0 && ! slices .Contains (r .ImageTypeTags , string (imageType )) {
254+ continue
255+ }
256+
257+ composerRepo , err := h .buildComposerRepositoryWithLatestSnapshot (r , repoMap )
258+ if err != nil {
259+ errs = append (errs , err )
260+ continue
261+ }
262+
263+ repositories = append (repositories , composerRepo )
264+ }
265+
266+ if len (errs ) > 0 {
267+ return nil , errors .Join (errs ... )
268+ }
269+ return repositories , nil
270+ }
271+
272+ func (h * Handlers ) buildComposerRepositoryWithLatestSnapshot (r distribution.Repository , repoMap map [string ]content_sources.ApiRepositoryResponse ) (composer.Repository , error ) {
273+ composerRepo := composer.Repository {
274+ Baseurl : r .Baseurl ,
275+ Metalink : r .Metalink ,
276+ Rhsm : common .ToPtr (r .Rhsm ),
277+ CheckGpg : r .CheckGpg ,
278+ Gpgkey : r .GpgKey ,
279+ }
280+
281+ if ! r .Rhsm {
282+ return composerRepo , nil
283+ }
284+
285+ var matchedRepo * content_sources.ApiRepositoryResponse
286+ for _ , apiRepo := range repoMap {
287+ if apiRepo .Url != nil && * apiRepo .Url == * r .Baseurl {
288+ matchedRepo = & apiRepo
289+ break
290+ }
291+ }
292+
293+ if matchedRepo != nil && matchedRepo .LatestSnapshotUrl != nil && * matchedRepo .LatestSnapshotUrl != "" {
294+ repoURL , err := url .Parse (* matchedRepo .LatestSnapshotUrl )
295+ if err != nil {
296+ return composer.Repository {}, fmt .Errorf ("failed to parse snapshot URL for repository %s: %w" , * r .Baseurl , err )
297+ }
298+ snapshotRepo , err := h .buildComposerRepositoryFromSnapshot (repoURL .Path , false , matchedRepo )
299+ if err != nil {
300+ return composer.Repository {}, err
301+ }
302+ return snapshotRepo , nil
303+ }
304+
305+ return composerRepo , nil
306+ }
307+
308+ func (h * Handlers ) buildComposerRepositoryFromSnapshot (snapshotPath string , usePrefix bool , apiRepo * content_sources.ApiRepositoryResponse ) (composer.Repository , error ) {
309+ composerRepo := composer.Repository {}
310+
311+ var baseurl string
312+ if usePrefix {
313+ baseurl = h .server .csReposURL .JoinPath (h .server .csReposPrefix , snapshotPath ).String ()
314+ } else {
315+ baseurl = h .server .csReposURL .JoinPath (snapshotPath ).String ()
316+ }
317+ composerRepo .Baseurl = common .ToPtr (baseurl )
318+ composerRepo .Rhsm = common .ToPtr (false )
319+
320+ if apiRepo != nil {
321+ if apiRepo .GpgKey != nil && * apiRepo .GpgKey != "" {
322+ composerRepo .Gpgkey = apiRepo .GpgKey
323+ }
324+ if composerRepo .Gpgkey != nil && * composerRepo .Gpgkey != "" {
325+ composerRepo .CheckGpg = common .ToPtr (true )
326+ }
327+ composerRepo .ModuleHotfixes = apiRepo .ModuleHotfixes
328+ composerRepo .CheckRepoGpg = apiRepo .MetadataVerification
329+ }
330+
331+ return composerRepo , nil
236332}
237333
238334func (h * Handlers ) buildRepositorySnapshots (ctx echo.Context , repoURLs []string , repoIDs []string , external bool , snapshotDate string ) ([]composer.Repository , []composer.CustomRepository , error ) {
@@ -287,26 +383,20 @@ func (h *Handlers) buildRepositorySnapshots(ctx echo.Context, repoURLs []string,
287383
288384 var repositories []composer.Repository
289385 var customRepositories []composer.CustomRepository
386+ var errs []error
387+
290388 for _ , snap := range * csSnapshots .Data {
291389 repo , ok := repoMap [* snap .RepositoryUuid ]
292390 if ! ok {
293- return repositories , customRepositories , fmt .Errorf ("returned snapshot %v unexpected repository id %v" , * snap .Match .Uuid , * snap .RepositoryUuid )
294- }
295-
296- composerRepo := composer.Repository {
297- // unlike latest snapshot URLs, the repository path of a snapshot match doesn't contain the path prefix
298- Baseurl : common .ToPtr (h .server .csReposURL .JoinPath (h .server .csReposPrefix , * snap .Match .RepositoryPath ).String ()),
299- Rhsm : common .ToPtr (false ),
391+ errs = append (errs , fmt .Errorf ("returned snapshot %v unexpected repository id %v" , * snap .Match .Uuid , * snap .RepositoryUuid ))
392+ continue
300393 }
301394
302- if repo .GpgKey != nil && * repo .GpgKey != "" {
303- composerRepo .Gpgkey = repo .GpgKey
304- }
305- if composerRepo .Gpgkey != nil && * composerRepo .Gpgkey != "" {
306- composerRepo .CheckGpg = common .ToPtr (true )
395+ composerRepo , err := h .buildComposerRepositoryFromSnapshot (* snap .Match .RepositoryPath , true , & repo )
396+ if err != nil {
397+ errs = append (errs , err )
398+ continue
307399 }
308- composerRepo .ModuleHotfixes = repo .ModuleHotfixes
309- composerRepo .CheckRepoGpg = repo .MetadataVerification
310400 repositories = append (repositories , composerRepo )
311401
312402 // Don't enable custom repositories, as they require further setup to be useable.
@@ -325,6 +415,10 @@ func (h *Handlers) buildRepositorySnapshots(ctx echo.Context, repoURLs []string,
325415 customRepositories = append (customRepositories , customRepo )
326416 }
327417
418+ if len (errs ) > 0 {
419+ return repositories , customRepositories , errors .Join (errs ... )
420+ }
421+
328422 ctx .Logger ().Debugf ("Resolved snapshots: %v" , repositories )
329423 return repositories , customRepositories , nil
330424}
0 commit comments