mirror of https://github.com/harness/drone.git
221 lines
6.8 KiB
Go
221 lines
6.8 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 generic
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"mime/multipart"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/harness/gitness/app/auth/authz"
|
|
corestore "github.com/harness/gitness/app/store"
|
|
"github.com/harness/gitness/registry/app/dist_temp/errcode"
|
|
"github.com/harness/gitness/registry/app/pkg"
|
|
"github.com/harness/gitness/registry/app/pkg/commons"
|
|
"github.com/harness/gitness/registry/app/pkg/filemanager"
|
|
"github.com/harness/gitness/registry/app/storage"
|
|
"github.com/harness/gitness/registry/app/store"
|
|
"github.com/harness/gitness/registry/app/store/database"
|
|
"github.com/harness/gitness/registry/types"
|
|
"github.com/harness/gitness/store/database/dbtx"
|
|
"github.com/harness/gitness/types/enum"
|
|
)
|
|
|
|
type Controller struct {
|
|
spaceStore corestore.SpaceStore
|
|
authorizer authz.Authorizer
|
|
DBStore *DBStore
|
|
fileManager filemanager.FileManager
|
|
tx dbtx.Transactor
|
|
}
|
|
|
|
type DBStore struct {
|
|
RegistryDao store.RegistryRepository
|
|
ImageDao store.ImageRepository
|
|
ArtifactDao store.ArtifactRepository
|
|
TagDao store.TagRepository
|
|
BandwidthStatDao store.BandwidthStatRepository
|
|
DownloadStatDao store.DownloadStatRepository
|
|
}
|
|
|
|
func NewController(
|
|
spaceStore corestore.SpaceStore,
|
|
authorizer authz.Authorizer,
|
|
fileManager filemanager.FileManager,
|
|
dBStore *DBStore,
|
|
tx dbtx.Transactor,
|
|
) *Controller {
|
|
return &Controller{
|
|
spaceStore: spaceStore,
|
|
authorizer: authorizer,
|
|
fileManager: fileManager,
|
|
DBStore: dBStore,
|
|
tx: tx,
|
|
}
|
|
}
|
|
|
|
func NewDBStore(
|
|
registryDao store.RegistryRepository,
|
|
imageDao store.ImageRepository,
|
|
artifactDao store.ArtifactRepository,
|
|
bandwidthStatDao store.BandwidthStatRepository,
|
|
downloadStatDao store.DownloadStatRepository,
|
|
) *DBStore {
|
|
return &DBStore{
|
|
RegistryDao: registryDao,
|
|
ImageDao: imageDao,
|
|
ArtifactDao: artifactDao,
|
|
BandwidthStatDao: bandwidthStatDao,
|
|
DownloadStatDao: downloadStatDao,
|
|
}
|
|
}
|
|
|
|
const regNameFormat = "registry : [%s]"
|
|
|
|
func (c Controller) UploadArtifact(ctx context.Context, info pkg.GenericArtifactInfo,
|
|
file multipart.File) (*commons.ResponseHeaders, errcode.Error) {
|
|
responseHeaders := &commons.ResponseHeaders{
|
|
Headers: make(map[string]string),
|
|
Code: 0,
|
|
}
|
|
err := pkg.GetRegistryCheckAccess(
|
|
ctx, c.DBStore.RegistryDao, c.authorizer, c.spaceStore, info.RegIdentifier, info.ParentID,
|
|
enum.PermissionArtifactsUpload,
|
|
)
|
|
if err != nil {
|
|
return nil, errcode.ErrCodeDenied.WithDetail(err)
|
|
}
|
|
|
|
path := info.Image + "/" + info.Version + "/" + info.FileName
|
|
fileInfo, err := c.fileManager.UploadFile(ctx, path, info.RegIdentifier, info.RegistryID,
|
|
info.RootParentID, info.RootIdentifier, file, nil, info.FileName)
|
|
if err != nil {
|
|
return responseHeaders, errcode.ErrCodeUnknown.WithDetail(err)
|
|
}
|
|
err = c.tx.WithTx(
|
|
ctx, func(ctx context.Context) error {
|
|
image := &types.Image{
|
|
Name: info.Image,
|
|
RegistryID: info.RegistryID,
|
|
Enabled: true,
|
|
}
|
|
err := c.DBStore.ImageDao.CreateOrUpdate(ctx, image)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create image for artifact : [%s] with "+
|
|
regNameFormat, info.Image, info.RegIdentifier)
|
|
}
|
|
|
|
dbArtifact, err := c.DBStore.ArtifactDao.GetByName(ctx, image.ID, info.Version)
|
|
|
|
if err != nil && !strings.Contains(err.Error(), "resource not found") {
|
|
return fmt.Errorf("failed to fetch artifact : [%s] with "+
|
|
regNameFormat, info.Image, info.RegIdentifier)
|
|
}
|
|
|
|
metadata := &database.GenericMetadata{
|
|
Description: info.Description,
|
|
}
|
|
err2 := c.updateMetadata(dbArtifact, metadata, info, fileInfo)
|
|
if err2 != nil {
|
|
return fmt.Errorf("failed to update metadata for artifact : [%s] with "+
|
|
regNameFormat, info.Image, info.RegIdentifier)
|
|
}
|
|
|
|
metadataJSON, err := json.Marshal(metadata)
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse metadata for artifact : [%s] with "+
|
|
regNameFormat, info.Image, info.RegIdentifier)
|
|
}
|
|
|
|
err = c.DBStore.ArtifactDao.CreateOrUpdate(ctx, &types.Artifact{
|
|
ImageID: image.ID,
|
|
Version: info.Version,
|
|
Metadata: metadataJSON,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create artifact : [%s] with "+
|
|
regNameFormat, info.Image, info.RegIdentifier)
|
|
}
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
return responseHeaders, errcode.ErrCodeUnknown.WithDetail(err)
|
|
}
|
|
responseHeaders.Code = http.StatusCreated
|
|
return responseHeaders, errcode.Error{}
|
|
}
|
|
|
|
func (c Controller) updateMetadata(dbArtifact *types.Artifact, metadata *database.GenericMetadata,
|
|
info pkg.GenericArtifactInfo, fileInfo pkg.FileInfo) error {
|
|
var files []database.File
|
|
if dbArtifact != nil {
|
|
err := json.Unmarshal(dbArtifact.Metadata, metadata)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get metadata for artifact : [%s] with registry : [%s]", info.Image, info.RegIdentifier)
|
|
}
|
|
fileExist := false
|
|
files = metadata.Files
|
|
for _, file := range files {
|
|
if file.Filename == info.FileName {
|
|
fileExist = true
|
|
}
|
|
}
|
|
if !fileExist {
|
|
files = append(files, database.File{Size: fileInfo.Size, Filename: fileInfo.Filename,
|
|
CreatedAt: time.Now().UnixMilli()})
|
|
metadata.Files = files
|
|
metadata.FileCount++
|
|
}
|
|
} else {
|
|
files = append(files, database.File{Size: fileInfo.Size, Filename: fileInfo.Filename,
|
|
CreatedAt: time.Now().UnixMilli()})
|
|
metadata.Files = files
|
|
metadata.FileCount++
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c Controller) PullArtifact(ctx context.Context, info pkg.GenericArtifactInfo) (*commons.ResponseHeaders,
|
|
*storage.FileReader, string, errcode.Error) {
|
|
responseHeaders := &commons.ResponseHeaders{
|
|
Headers: make(map[string]string),
|
|
Code: 0,
|
|
}
|
|
err := pkg.GetRegistryCheckAccess(
|
|
ctx, c.DBStore.RegistryDao, c.authorizer, c.spaceStore, info.RegIdentifier, info.ParentID,
|
|
enum.PermissionArtifactsDownload,
|
|
)
|
|
if err != nil {
|
|
return nil, nil, "", errcode.ErrCodeDenied.WithDetail(err)
|
|
}
|
|
|
|
path := "/" + info.Image + "/" + info.Version + "/" + info.FileName
|
|
fileReader, _, redirectURL, err := c.fileManager.DownloadFile(ctx, path, types.Registry{
|
|
ID: info.RegistryID,
|
|
Name: info.RootIdentifier,
|
|
}, info.RootIdentifier)
|
|
if err != nil {
|
|
return responseHeaders, nil, "", errcode.ErrCodeUnknown.WithDetail(err)
|
|
}
|
|
responseHeaders.Code = http.StatusOK
|
|
return responseHeaders, fileReader, redirectURL, errcode.Error{}
|
|
}
|