fix: [AH-674]: support upstream proxies at org/acct, multiple levels (#3055)

* fix: [AH-674]: support upstream proxies at org/acct, multiple levels. change the default to false:non-recursive
* fix: [AH-674]: support upstream proxies at org/acct, multiple levels. add a flag in API for recursive or non-recursive
* fix: [AH-674]: support upstream proxies at org/acct, multiple levels. combine recursive and non-recursive for better maintance, optimze quries
* fix: [AH-674]: support upstream proxies at org/acct, multiple levels
* fix: [AH-674]: support upstream proxies at org/acct, multiple levels
This commit is contained in:
Shivakumar Ningappa 2024-11-29 01:54:49 +00:00 committed by Harness
parent f0d58cdda7
commit facd2431a3
8 changed files with 296 additions and 115 deletions

View File

@ -27,7 +27,7 @@ import (
"github.com/harness/gitness/registry/app/storage"
"github.com/harness/gitness/registry/types"
"github.com/opencontainers/go-digest"
digest "github.com/opencontainers/go-digest"
"github.com/rs/zerolog/log"
)
@ -58,6 +58,7 @@ type RegistryRequestInfo struct {
searchTerm string
labels []string
registryIDs []string
recursive bool
}
type RegistryRequestParams struct {
@ -72,6 +73,7 @@ type RegistryRequestParams struct {
sortOrder *api.SortOrder
sortField *api.SortField
registryIDsParam *api.RegistryIdentifierParam
recursive bool
}
// GetRegistryRequestBaseInfo returns the base info for the registry request
@ -186,6 +188,7 @@ func (c *APIController) GetRegistryRequestInfo(
searchTerm: searchTerm,
labels: labels,
registryIDs: registryIDs,
recursive: registryRequestParams.recursive,
}, nil
}

View File

@ -45,6 +45,7 @@ func (c *APIController) GetAllRegistries(
sortOrder: r.Params.SortOrder,
sortField: r.Params.SortField,
registryIDsParam: nil,
recursive: r.Params.Recursive != nil && bool(*r.Params.Recursive), // default is false
}
regInfo, _ := c.GetRegistryRequestInfo(ctx, *registryRequestParams)
@ -97,6 +98,7 @@ func (c *APIController) GetAllRegistries(
regInfo.offset,
regInfo.searchTerm,
repoType,
regInfo.recursive,
)
count, _ = c.RegistryRepository.CountAll(
ctx,

View File

@ -38,6 +38,7 @@ paths:
- $ref: "#/components/parameters/sortOrder"
- $ref: "#/components/parameters/sortField"
- $ref: "#/components/parameters/searchTerm"
- $ref: "#/components/parameters/recursiveParam"
responses:
200:
$ref: "#/components/responses/ListRegistryResponse"
@ -1661,6 +1662,14 @@ components:
type: array
items:
type: string
recursiveParam:
name: recursive
in: query
description: Whether to list registries recursively.
required: false
schema:
type: boolean
default: false
spaceRefPathParam:
name: space_ref
in: path

View File

@ -1,6 +1,6 @@
// Package artifact provides primitives to interact with the openapi HTTP API.
//
// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.1.0 DO NOT EDIT.
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.3.0 DO NOT EDIT.
package artifact
import (
@ -1435,6 +1435,14 @@ func (siw *ServerInterfaceWrapper) GetAllRegistries(w http.ResponseWriter, r *ht
return
}
// ------------- Optional query parameter "recursive" -------------
err = runtime.BindQueryParameter("form", true, false, "recursive", r.URL.Query(), &params.Recursive)
if err != nil {
siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "recursive", Err: err})
return
}
handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
siw.Handler.GetAllRegistries(w, r, spaceRef, params)
}))
@ -4171,71 +4179,73 @@ func (sh *strictHandler) GetAllRegistries(w http.ResponseWriter, r *http.Request
// Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{
"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",
"2HfKFt6eAAA=",
}
// GetSwagger returns the content of the embedded swagger specification file

View File

@ -1,6 +1,6 @@
// Package artifact provides primitives to interact with the openapi HTTP API.
//
// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.1.0 DO NOT EDIT.
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.3.0 DO NOT EDIT.
package artifact
import (
@ -494,6 +494,9 @@ type PageNumber int64
// PageSize defines model for pageSize.
type PageSize int64
// RecursiveParam defines model for recursiveParam.
type RecursiveParam bool
// RegistryRefPathParam defines model for registryRefPathParam.
type RegistryRefPathParam string
@ -855,6 +858,9 @@ type GetAllRegistriesParams struct {
// SearchTerm search Term.
SearchTerm *SearchTerm `form:"search_term,omitempty" json:"search_term,omitempty"`
// Recursive Whether to list registries recursively.
Recursive *RecursiveParam `form:"recursive,omitempty" json:"recursive,omitempty"`
}
// GetAllRegistriesParamsType defines parameters for GetAllRegistries.

View File

@ -366,6 +366,7 @@ type RegistryRepository interface {
offset int,
search string,
repoType string,
recursive bool,
) (repos *[]RegistryMetadata, err error)
CountAll(

View File

@ -33,6 +33,7 @@ import (
sq "github.com/Masterminds/squirrel"
"github.com/jmoiron/sqlx"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
)
type registryDao struct {
@ -231,66 +232,160 @@ func (r registryDao) GetAll(
offset int,
search string,
repoType string,
recursive bool,
) (repos *[]store.RegistryMetadata, err error) {
q := databaseg.Builder.Select(
"r.registry_name as reg_identifier,"+
" r.registry_description as description , "+
"r.registry_package_type as package_type, r.registry_type as type, r.registry_updated_at as last_modified,"+
" u.upstream_proxy_config_url as url, COALESCE(t2.artifact_count,0) as artifact_count, "+
"COALESCE(t3.size,0) as size , r.registry_labels, COALESCE(t4.download_count,0) as download_count ",
).
From("registries r").
LeftJoin("upstream_proxy_configs u ON r.registry_id = u.upstream_proxy_config_registry_id").
LeftJoin(
"(SELECT r.registry_id, count(a.image_id) as artifact_count FROM"+
" registries r LEFT JOIN images a ON r.registry_id = a.image_registry_id"+
" WHERE r.registry_parent_id = ? AND a.image_enabled = true GROUP BY r.registry_id ) as t2"+
" ON r.registry_id = t2.registry_id ", parentID,
).
LeftJoin(
"(SELECT r.registry_id , COALESCE(sum(b.blob_size),0) as size FROM "+
"registries r LEFT JOIN registry_blobs rb ON r.registry_id = rblob_registry_id "+
"LEFT JOIN blobs b ON rblob_blob_id = b.blob_id WHERE r.registry_parent_id = ? "+
"GROUP BY r.registry_id) as t3 ON r.registry_id = t3.registry_id ", parentID,
).
LeftJoin(
"(SELECT i.image_registry_id, COUNT(d.download_stat_id) as download_count "+
"FROM artifacts a "+
" JOIN images i on i.image_id = a.artifact_image_id"+
" JOIN download_stats d ON d.download_stat_artifact_id = a.artifact_id"+
" WHERE i.image_enabled = true GROUP BY i.image_registry_id ) as t4 "+
" ON r.registry_id = t4.image_registry_id").
Where("r.registry_parent_id = ?", parentID)
// Select only required fields
selectFields := `
r.registry_name AS reg_identifier,
COALESCE(r.registry_description, '') AS description,
r.registry_package_type AS package_type,
r.registry_type AS type,
r.registry_updated_at AS last_modified,
COALESCE(u.upstream_proxy_config_url, '') AS url,
COALESCE(artifact_count.count, 0) AS artifact_count,
COALESCE(blob_sizes.total_size, 0) AS size,
r.registry_labels,
COALESCE(download_stats.download_count, 0) AS download_count
`
if search != "" {
q = q.Where("r.registry_name LIKE ?", "%"+search+"%")
}
// Subqueries with optimizations for reduced joins and grouping
artifactCountSubquery := `
SELECT image_registry_id, COUNT(image_id) AS count
FROM images
WHERE image_enabled = TRUE
GROUP BY 1
`
if len(packageTypes) > 0 {
q = q.Where(sq.Eq{"r.registry_package_type": packageTypes})
}
if repoType != "" {
q = q.Where("r.registry_type = ?", repoType)
}
blobSizesSubquery := `
SELECT rblob_registry_id AS rblob_registry_id, SUM(b.blob_size) AS total_size
FROM registry_blobs rb
JOIN blobs b ON rb.rblob_blob_id = b.blob_id
GROUP BY 1
`
if sortByField == "artifact_count" || sortByField == "size" || sortByField == "download_count" {
q = q.OrderBy(sortByField + " " + sortByOrder).Limit(uint64(limit)).Offset(uint64(offset))
downloadStatsSubquery := `
SELECT i.image_registry_id AS registry_id, COUNT(d.download_stat_id) AS download_count
FROM download_stats d
JOIN artifacts a ON d.download_stat_artifact_id = a.artifact_id
JOIN images i ON a.artifact_image_id = i.image_id
WHERE i.image_enabled = TRUE
GROUP BY 1
`
cte := `
WITH RECURSIVE registry_hierarchy AS (
-- Base case: Start with nodes having registry_parent_id = $1
SELECT
r.registry_id,
r.registry_parent_id,
r.registry_root_parent_id,
s.space_parent_id AS registry_parent_parent_id,
r.registry_name,
1::integer AS recursion_level, -- Initialize recursion level
ARRAY[r.registry_id] AS path -- Track visited nodes
FROM
registries r
LEFT JOIN
spaces s ON r.registry_parent_id = s.space_id -- Fetch registry_parent_parent_id from spaces
WHERE
r.registry_parent_id = %d
UNION
-- Recursive step: Traverse the hierarchy upward to the root
SELECT
r.registry_id,
r.registry_parent_id,
r.registry_root_parent_id,
s.space_parent_id AS registry_parent_parent_id,
r.registry_name,
rh.recursion_level + 1 AS recursion_level, -- Increment recursion level
rh.path || r.registry_id -- Append current node to the path
FROM
registries r
LEFT JOIN
spaces s ON r.registry_parent_id = s.space_id -- Fetch registry_parent_parent_id
INNER JOIN
registry_hierarchy rh ON rh.registry_parent_parent_id = r.registry_parent_id -- Match parent to child
WHERE
NOT r.registry_id = ANY(rh.path) -- Avoid revisiting nodes
AND rh.registry_parent_parent_id IS NOT NULL
AND rh.recursion_level < 10 -- Limit recursion depth
),
registry_hierarchy_u AS (
SELECT DISTINCT registry_id FROM registry_hierarchy)
`
cte = fmt.Sprintf(cte, parentID)
var query sq.SelectBuilder
if recursive {
query = databaseg.Builder.
Select(selectFields).
From("registry_hierarchy_u rh").
InnerJoin("registries r ON rh.registry_id = r.registry_id").
LeftJoin("upstream_proxy_configs u ON r.registry_id = u.upstream_proxy_config_registry_id").
LeftJoin(fmt.Sprintf("(%s) AS artifact_count ON r.registry_id = artifact_count.image_registry_id",
artifactCountSubquery)).
LeftJoin(fmt.Sprintf("(%s) AS blob_sizes ON r.registry_id = blob_sizes.rblob_registry_id", blobSizesSubquery)).
LeftJoin(fmt.Sprintf("(%s) AS download_stats ON r.registry_id = download_stats.registry_id", downloadStatsSubquery))
} else {
q = q.OrderBy("r.registry_" + sortByField + " " + sortByOrder).
Limit(uint64(limit)).Offset(uint64(offset))
query = databaseg.Builder.
Select(selectFields).
From("registries r").
LeftJoin("upstream_proxy_configs u ON r.registry_id = u.upstream_proxy_config_registry_id").
LeftJoin(fmt.Sprintf("(%s) AS artifact_count ON r.registry_id = artifact_count.image_registry_id",
artifactCountSubquery)).
LeftJoin(fmt.Sprintf("(%s) AS blob_sizes ON r.registry_id = blob_sizes.rblob_registry_id", blobSizesSubquery)).
LeftJoin(fmt.Sprintf("(%s) AS download_stats ON r.registry_id = download_stats.registry_id", downloadStatsSubquery)).
Where("r.registry_parent_id = ?", parentID)
}
sql, args, err := q.ToSql()
// Apply search filter
if search != "" {
query = query.Where("r.registry_name LIKE ?", "%"+search+"%")
}
// Apply package types filter
if len(packageTypes) > 0 {
query = query.Where(sq.Eq{"r.registry_package_type": packageTypes})
}
// Apply repository type filter
if repoType != "" {
query = query.Where("r.registry_type = ?", repoType)
}
// Sorting and pagination
validSortFields := map[string]bool{
"artifact_count": true,
"size": true,
"download_count": true,
}
if validSortFields[sortByField] {
query = query.OrderBy(fmt.Sprintf("%s %s", sortByField, sortByOrder))
} else {
query = query.OrderBy(fmt.Sprintf("r.registry_%s %s", sortByField, sortByOrder))
}
query = query.Limit(uint64(limit)).Offset(uint64(offset))
// Convert query to SQL
sql, args, err := query.ToSql()
if err != nil {
return nil, errors.Wrap(err, "Failed to convert query to sql")
return nil, errors.Wrap(err, "Failed to convert query to SQL")
}
sql = cte + sql // add CTE to the query
// Log the final query
finalQuery := util.ConstructQuery(sql, args)
log.Ctx(ctx).Debug().
Str("sql", finalQuery).
Msg("Executing query")
// Execute query
db := dbtx.GetAccessor(ctx, r.db)
dst := []*RegistryMetadataDB{}
if err = db.SelectContext(ctx, &dst, sql, args...); err != nil {
return nil, databaseg.ProcessSQLErrorf(ctx, err, "Failed executing custom list query")
if err := db.SelectContext(ctx, &dst, sql, args...); err != nil {
return nil, databaseg.ProcessSQLErrorf(ctx, err, "Failed executing query")
}
// Map results to response type
return r.mapToRegistryMetadataList(ctx, dst)
}

View File

@ -16,6 +16,8 @@ package util
import (
"database/sql"
"fmt"
"strings"
"github.com/harness/gitness/registry/app/pkg/commons"
)
@ -33,3 +35,56 @@ func GetEmptySQLInt32(i int) sql.NullInt32 {
}
return sql.NullInt32{Int32: int32(i), Valid: true}
}
func ConstructQuery(query string, args []interface{}) string {
var builder strings.Builder
argIndex := 0
for i := 0; i < len(query); i++ {
if query[i] == '?' && argIndex < len(args) {
arg := args[argIndex]
argIndex++
// Convert the argument to a SQL-safe string
var argStr string
switch v := arg.(type) {
case string:
argStr = fmt.Sprintf("'%s'", strings.ReplaceAll(v, "'", "''")) // Escape single quotes in strings
case int, int64, float64:
argStr = fmt.Sprintf("%v", v)
case bool:
argStr = fmt.Sprintf("%t", v)
default:
argStr = fmt.Sprintf("'%v'", v)
}
builder.WriteString(argStr)
} else {
builder.WriteByte(query[i])
}
}
return builder.String()
}
// FormatQuery is a helper function to interpolate parameters into the query.
func FormatQuery(query string, params []interface{}) string {
for i, param := range params {
placeholder := fmt.Sprintf("$%d", i+1)
var value string
switch v := param.(type) {
case string:
value = fmt.Sprintf("'%s'", strings.ReplaceAll(v, "'", "''"))
case []string:
quotedValues := make([]string, len(v))
for i, s := range v {
quotedValues[i] = fmt.Sprintf("'%s'", strings.ReplaceAll(s, "'", "''"))
}
value = fmt.Sprintf("ARRAY[%s]", strings.Join(quotedValues, ", "))
default:
value = fmt.Sprintf("%v", v)
}
query = strings.Replace(query, placeholder, value, 1)
}
return query
}