mirror of https://github.com/harness/drone.git
355 lines
10 KiB
Go
355 lines
10 KiB
Go
// Copyright 2023 Harness, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package metadata
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/harness/gitness/app/paths"
|
|
api "github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
|
|
storagedriver "github.com/harness/gitness/registry/app/driver"
|
|
"github.com/harness/gitness/registry/app/pkg/commons"
|
|
"github.com/harness/gitness/registry/app/storage"
|
|
"github.com/harness/gitness/registry/types"
|
|
|
|
"github.com/opencontainers/go-digest"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
const MediaTypeImageConfig = "application/vnd.docker.container.image.v1+json"
|
|
|
|
var _ api.StrictServerInterface = (*APIController)(nil)
|
|
|
|
type RegistryRequestBaseInfo struct {
|
|
rootIdentifier string
|
|
rootIdentifierID int64
|
|
|
|
registryRef string
|
|
RegistryIdentifier string
|
|
registryID int64
|
|
|
|
parentRef string
|
|
parentID int64
|
|
}
|
|
|
|
type RegistryRequestInfo struct {
|
|
RegistryRequestBaseInfo
|
|
packageTypes []string
|
|
sortByField string
|
|
sortByOrder string
|
|
offset int
|
|
limit int
|
|
pageNumber int64
|
|
searchTerm string
|
|
labels []string
|
|
}
|
|
|
|
// GetRegistryRequestBaseInfo returns the base info for the registry request
|
|
// One of the regRefParam or (parentRefParam + regIdentifierParam) should be provided.
|
|
func (c *APIController) GetRegistryRequestBaseInfo(
|
|
ctx context.Context,
|
|
parentRef string,
|
|
regRef string,
|
|
) (*RegistryRequestBaseInfo, error) {
|
|
// ---------- CHECKS ------------
|
|
if commons.IsEmpty(parentRef) && !commons.IsEmpty(regRef) {
|
|
parentRef, _, _ = paths.DisectLeaf(regRef)
|
|
}
|
|
|
|
// ---------- PARENT ------------
|
|
if commons.IsEmpty(parentRef) {
|
|
return nil, fmt.Errorf("parent reference is required")
|
|
}
|
|
rootIdentifier, _, err := paths.DisectRoot(parentRef)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid parent reference: %w", err)
|
|
}
|
|
|
|
rootSpace, err := c.spaceStore.FindByRef(ctx, rootIdentifier)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("root space not found: %w", err)
|
|
}
|
|
parentSpace, err := c.spaceStore.FindByRef(ctx, parentRef)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parent space not found: %w", err)
|
|
}
|
|
rootIdentifierID := rootSpace.ID
|
|
parentID := parentSpace.ID
|
|
|
|
baseInfo := &RegistryRequestBaseInfo{
|
|
parentRef: parentRef,
|
|
parentID: parentID,
|
|
rootIdentifier: rootIdentifier,
|
|
rootIdentifierID: rootIdentifierID,
|
|
}
|
|
|
|
// ---------- REGISTRY ------------
|
|
if !commons.IsEmpty(regRef) {
|
|
_, regIdentifier, _ := paths.DisectLeaf(regRef)
|
|
|
|
reg, getRegistryErr := c.RegistryRepository.GetByParentIDAndName(ctx, parentID, regIdentifier)
|
|
if getRegistryErr != nil {
|
|
return nil, fmt.Errorf("registry not found: %w", err)
|
|
}
|
|
|
|
baseInfo.registryRef = regRef
|
|
baseInfo.RegistryIdentifier = regIdentifier
|
|
baseInfo.registryID = reg.ID
|
|
}
|
|
|
|
return baseInfo, nil
|
|
}
|
|
|
|
func (c *APIController) GetRegistryRequestInfo(
|
|
ctx context.Context,
|
|
packageTypesParam *api.PackageTypeParam,
|
|
page *api.PageNumber,
|
|
size *api.PageSize,
|
|
search *api.SearchTerm,
|
|
resource string,
|
|
parentRef string,
|
|
regRef string,
|
|
labelsParam *api.LabelsParam,
|
|
sortOrder *api.SortOrder,
|
|
sortField *api.SortField,
|
|
) (*RegistryRequestInfo, error) {
|
|
packageTypes := []string{}
|
|
if packageTypesParam != nil {
|
|
packageTypes = *packageTypesParam
|
|
}
|
|
sortByField := ""
|
|
sortByOrder := ""
|
|
if sortOrder != nil {
|
|
sortByOrder = string(*sortOrder)
|
|
}
|
|
|
|
if sortField != nil {
|
|
sortByField = string(*sortField)
|
|
}
|
|
|
|
labels := []string{}
|
|
|
|
if labelsParam != nil {
|
|
labels = *labelsParam
|
|
}
|
|
|
|
sortByField = GetSortByField(sortByField, resource)
|
|
sortByOrder = GetSortByOrder(sortByOrder)
|
|
|
|
offset := GetOffset(size, page)
|
|
limit := GetPageLimit(size)
|
|
pageNumber := GetPageNumber(page)
|
|
|
|
searchTerm := ""
|
|
if search != nil {
|
|
searchTerm = string(*search)
|
|
}
|
|
|
|
baseInfo, err := c.GetRegistryRequestBaseInfo(ctx, parentRef, regRef)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &RegistryRequestInfo{
|
|
RegistryRequestBaseInfo: *baseInfo,
|
|
packageTypes: packageTypes,
|
|
sortByField: sortByField,
|
|
sortByOrder: sortByOrder,
|
|
offset: offset,
|
|
limit: limit,
|
|
pageNumber: pageNumber,
|
|
searchTerm: searchTerm,
|
|
labels: labels,
|
|
}, nil
|
|
}
|
|
|
|
func getManifestConfig(
|
|
ctx context.Context,
|
|
digest digest.Digest,
|
|
rootRef string,
|
|
driver storagedriver.StorageDriver,
|
|
) (*manifestConfig, error) {
|
|
var config manifestConfig
|
|
path, err := storage.PathFn(strings.ToLower(rootRef), digest)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get path: %w", err)
|
|
}
|
|
|
|
content, err := driver.GetContent(ctx, path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get content for image config: %w", err)
|
|
}
|
|
if err := json.Unmarshal(content, &config); err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal manifest config: %w", err)
|
|
}
|
|
|
|
return &config, nil
|
|
}
|
|
|
|
func (c *APIController) setUpstreamProxyIDs(
|
|
ctx context.Context,
|
|
registry *types.Registry,
|
|
dto api.RegistryRequest,
|
|
parentID int64,
|
|
) error {
|
|
if dto.Config.Type != api.RegistryTypeVIRTUAL {
|
|
return fmt.Errorf("invalid call to set upstream proxy ids for parentID: %d", parentID)
|
|
}
|
|
virtualConfig, err := dto.Config.AsVirtualConfig()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get virtualConfig: %w", err)
|
|
}
|
|
if nil == virtualConfig.UpstreamProxies || commons.IsEmpty(*(virtualConfig.UpstreamProxies)) {
|
|
log.Ctx(ctx).Debug().Msgf("Nothing to do for registryRequest: %s", dto.Identifier)
|
|
return nil
|
|
}
|
|
|
|
upstreamProxies, err := c.RegistryRepository.FetchUpstreamProxyIDs(
|
|
ctx,
|
|
*virtualConfig.UpstreamProxies,
|
|
parentID,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to fectch upstream proxy IDs :%w", err)
|
|
}
|
|
registry.UpstreamProxies = upstreamProxies
|
|
return nil
|
|
}
|
|
|
|
func (c *APIController) getUpstreamProxyKeys(ctx context.Context, ids []int64) []string {
|
|
repoKeys, _ := c.RegistryRepository.FetchUpstreamProxyKeys(ctx, ids)
|
|
return repoKeys
|
|
}
|
|
|
|
type manifestConfig struct {
|
|
CreatedAt *string `json:"created,omitempty"`
|
|
Digest string `json:"digest,omitempty"`
|
|
History []historyEntry `json:"history"`
|
|
ModifiedAt *string `json:"modified,omitempty"`
|
|
Os string `json:"os"`
|
|
Arch string `json:"architecture,omitempty"`
|
|
}
|
|
|
|
type historyEntry struct {
|
|
Created string `json:"created"`
|
|
CreatedBy string `json:"created_by"`
|
|
EmptyLayer bool `json:"empty_layer"`
|
|
Comment string `json:"comment,omitempty"`
|
|
}
|
|
|
|
func getRepoEntityFields(dto api.RegistryRequest) ([]string, []string, string, []string) {
|
|
allowedPattern := []string{}
|
|
if dto.AllowedPattern != nil {
|
|
allowedPattern = *dto.AllowedPattern
|
|
}
|
|
blockedPattern := []string{}
|
|
if dto.BlockedPattern != nil {
|
|
blockedPattern = *dto.BlockedPattern
|
|
}
|
|
description := ""
|
|
if dto.Description != nil {
|
|
description = *dto.Description
|
|
}
|
|
labels := []string{}
|
|
if dto.Labels != nil {
|
|
labels = *dto.Labels
|
|
}
|
|
return allowedPattern, blockedPattern, description, labels
|
|
}
|
|
|
|
func CreateVirtualRepositoryResponse(
|
|
registry *types.Registry,
|
|
upstreamProxyKeys []string,
|
|
cleanupPolicies *[]types.CleanupPolicy,
|
|
rootIdentifier string,
|
|
registryURL string,
|
|
) *api.RegistryResponseJSONResponse {
|
|
createdAt := GetTimeInMs(registry.CreatedAt)
|
|
modifiedAt := GetTimeInMs(registry.UpdatedAt)
|
|
allowedPattern := registry.AllowedPattern
|
|
blockedPattern := registry.BlockedPattern
|
|
labels := registry.Labels
|
|
|
|
config := api.RegistryConfig{}
|
|
_ = config.FromVirtualConfig(api.VirtualConfig{UpstreamProxies: &upstreamProxyKeys})
|
|
response := &api.RegistryResponseJSONResponse{
|
|
Data: api.Registry{
|
|
Identifier: registry.Name,
|
|
Description: ®istry.Description,
|
|
Url: GetRepoURL(rootIdentifier, registry.Name, registryURL),
|
|
PackageType: registry.PackageType,
|
|
AllowedPattern: &allowedPattern,
|
|
BlockedPattern: &blockedPattern,
|
|
CreatedAt: &createdAt,
|
|
ModifiedAt: &modifiedAt,
|
|
CleanupPolicy: CreateCleanupPolicyResponse(cleanupPolicies),
|
|
Config: &config,
|
|
Labels: &labels,
|
|
},
|
|
Status: api.StatusSUCCESS,
|
|
}
|
|
return response
|
|
}
|
|
|
|
func CreateUpstreamProxyResponseJSONResponse(upstreamproxy *types.UpstreamProxy) *api.RegistryResponseJSONResponse {
|
|
createdAt := GetTimeInMs(upstreamproxy.CreatedAt)
|
|
modifiedAt := GetTimeInMs(upstreamproxy.UpdatedAt)
|
|
allowedPattern := upstreamproxy.AllowedPattern
|
|
blockedPattern := upstreamproxy.BlockedPattern
|
|
configAuth := &api.UpstreamConfig_Auth{}
|
|
|
|
if api.AuthType(upstreamproxy.RepoAuthType) == api.AuthTypeUserPassword {
|
|
auth := api.UserPassword{}
|
|
auth.UserName = upstreamproxy.UserName
|
|
auth.SecretIdentifier = &upstreamproxy.SecretIdentifier.String
|
|
auth.SecretSpaceId = nil
|
|
if upstreamproxy.SecretSpaceID.Valid {
|
|
// Convert int32 to int and assign to the expected field
|
|
secretSpaceID := int(upstreamproxy.SecretSpaceID.Int32)
|
|
auth.SecretSpaceId = &secretSpaceID
|
|
}
|
|
_ = configAuth.FromUserPassword(auth)
|
|
}
|
|
|
|
source := api.UpstreamConfigSource(upstreamproxy.Source)
|
|
|
|
config := api.UpstreamConfig{
|
|
AuthType: api.AuthType(upstreamproxy.RepoAuthType),
|
|
Auth: configAuth,
|
|
Source: &source,
|
|
Url: &upstreamproxy.RepoURL,
|
|
}
|
|
registryConfig := &api.RegistryConfig{}
|
|
_ = registryConfig.FromUpstreamConfig(config)
|
|
|
|
response := &api.RegistryResponseJSONResponse{
|
|
Data: api.Registry{
|
|
Identifier: upstreamproxy.RepoKey,
|
|
PackageType: upstreamproxy.PackageType,
|
|
Url: upstreamproxy.RepoURL,
|
|
AllowedPattern: &allowedPattern,
|
|
BlockedPattern: &blockedPattern,
|
|
CreatedAt: &createdAt,
|
|
ModifiedAt: &modifiedAt,
|
|
Config: registryConfig,
|
|
},
|
|
Status: api.StatusSUCCESS,
|
|
}
|
|
return response
|
|
}
|