Files
zot/pkg/cli/client/discover.go
T
Luca Muscariello 2402296e9a fix: migrate to Go module v2 for proper semantic versioning (#3462)
* fix: migrate to Go module v2 for proper semantic versioning

This change updates the module path from 'zotregistry.dev/zot' to
'zotregistry.dev/zot/v2' to comply with Go's semantic versioning rules.

According to Go's module versioning requirements, major version v2+
must include the major version in the module path. The current
module path 'zotregistry.dev/zot' only supports v0.x.x and v1.x.x
versions, making existing v2.x.x tags (like v2.1.8) unusable.

Changes:
- Updated go.mod module path to zotregistry.dev/zot/v2
- Updated all internal import paths across 280+ Go source files
- Updated configuration files (golangcilint.yaml, gqlgen.yml)
- Updated README.md Go reference badge

This fix enables proper use of existing v2.x.x Git tags and allows
external packages to import zot v2+ versions without compatibility
errors.

Resolves: Go module import compatibility for v2+ versions
Fixes: #3071
Signed-off-by: Luca Muscariello <muscariello@ieee.org>

* fix: regenerate GraphQL files with updated v2 import paths

The gqlgen tool needs to regenerate the GraphQL schema files after
the module path change to use the new v2 imports.

Signed-off-by: Luca Muscariello <muscariello@ieee.org>

---------

Signed-off-by: Luca Muscariello <muscariello@ieee.org>
2025-10-16 22:43:47 -07:00

169 lines
3.9 KiB
Go

//go:build search
// +build search
package client
import (
"context"
"fmt"
distext "github.com/opencontainers/distribution-spec/specs-go/v1/extensions"
zerr "zotregistry.dev/zot/v2/errors"
"zotregistry.dev/zot/v2/pkg/api/constants"
zcommon "zotregistry.dev/zot/v2/pkg/common"
)
type field struct {
Name string `json:"name"`
Args []struct {
Name string `json:"name"`
} `json:"args"`
}
type schemaList struct {
Data struct {
Schema struct {
QueryType struct {
Fields []field `json:"fields"`
} `json:"queryType"` //nolint:tagliatelle // graphQL schema
Types []typeInfo `json:"types"`
} `json:"__schema"` //nolint:tagliatelle // graphQL schema
} `json:"data"`
Errors []zcommon.ErrorGQL `json:"errors"`
}
type typeInfo struct {
Name string `json:"name"`
Fields []typeField `json:"fields"`
}
type typeField struct {
Name string `json:"name"`
}
func containsGQLQueryWithParams(queryList []field, serverGQLTypesList []typeInfo, requiredQueries ...GQLQuery) error {
serverGQLTypes := map[string][]typeField{}
for _, typeInfo := range serverGQLTypesList {
serverGQLTypes[typeInfo.Name] = typeInfo.Fields
}
for _, reqQuery := range requiredQueries {
foundQuery := false
for _, query := range queryList {
if query.Name == reqQuery.Name && haveSameArgs(query, reqQuery) {
foundQuery = true
}
}
if !foundQuery {
return fmt.Errorf("%w: %s", zerr.ErrGQLQueryNotSupported, reqQuery.Name)
}
// let's check just the name of the returned type
returnType := reqQuery.ReturnType.Name
// we can next define fields of the returned types and check them recursively
// for now we will just check the name of the returned type to be known by the server
_, ok := serverGQLTypes[returnType]
if !ok {
return fmt.Errorf("%w: server doesn't support needed type '%s'", zerr.ErrGQLQueryNotSupported, returnType)
}
}
return nil
}
func haveSameArgs(query field, reqQuery GQLQuery) bool {
if len(query.Args) != len(reqQuery.Args) {
return false
}
for i := range query.Args {
if query.Args[i].Name != reqQuery.Args[i] {
return false
}
}
return true
}
func CheckExtEndPointQuery(config SearchConfig, requiredQueries ...GQLQuery) error {
username, password := getUsernameAndPassword(config.User)
ctx := context.Background()
discoverEndPoint, err := combineServerAndEndpointURL(config.ServURL, fmt.Sprintf("%s%s",
constants.RoutePrefix, constants.ExtOciDiscoverPrefix))
if err != nil {
return err
}
discoverResponse := &distext.ExtensionList{}
_, err = makeGETRequest(ctx, discoverEndPoint, username, password, config.VerifyTLS,
config.Debug, &discoverResponse, config.ResultWriter)
if err != nil {
return err
}
searchEnabled := false
for _, extension := range discoverResponse.Extensions {
if extension.Name == constants.BaseExtension {
for _, endpoint := range extension.Endpoints {
if endpoint == constants.FullSearchPrefix {
searchEnabled = true
}
}
}
}
if !searchEnabled {
return fmt.Errorf("%w: search extension gql endpoints not found", zerr.ErrExtensionNotEnabled)
}
searchEndPoint, _ := combineServerAndEndpointURL(config.ServURL, constants.FullSearchPrefix)
schemaQuery := `
{
__schema {
queryType {
fields {
name
args {
name
}
type {
name
kind
}
}
__typename
}
types {
name
fields {
name
}
}
}
}`
queryResponse := &schemaList{}
err = makeGraphQLRequest(ctx, searchEndPoint, schemaQuery, username, password, config.VerifyTLS,
config.Debug, queryResponse, config.ResultWriter)
if err != nil {
return fmt.Errorf("gql query failed: %w", err)
}
if err = checkResultGraphQLQuery(ctx, err, queryResponse.Errors); err != nil {
return fmt.Errorf("gql query failed: %w", err)
}
return containsGQLQueryWithParams(queryResponse.Data.Schema.QueryType.Fields,
queryResponse.Data.Schema.Types, requiredQueries...)
}