mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/yunionio/cloudpods.git
synced 2026-09-20 08:03:53 +08:00
Move container registry management into glance with import from kubeserver, add container_image model, resolve container_image_id in compute, and switch dashboard create flow to glance.
302 lines
9.9 KiB
Go
302 lines
9.9 KiB
Go
// Copyright 2019 Yunion
|
|
//
|
|
// 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 models
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"yunion.io/x/jsonutils"
|
|
"yunion.io/x/log"
|
|
"yunion.io/x/pkg/errors"
|
|
"yunion.io/x/sqlchemy"
|
|
|
|
identityapi "yunion.io/x/onecloud/pkg/apis/identity"
|
|
api "yunion.io/x/onecloud/pkg/apis/image"
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
|
"yunion.io/x/onecloud/pkg/httperrors"
|
|
"yunion.io/x/onecloud/pkg/image/options"
|
|
"yunion.io/x/onecloud/pkg/mcclient"
|
|
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
|
"yunion.io/x/onecloud/pkg/mcclient/modules/identity"
|
|
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
|
)
|
|
|
|
func init() {
|
|
GetContainerImageManager()
|
|
}
|
|
|
|
var containerImageManager *SContainerImageManager
|
|
|
|
func GetContainerImageManager() *SContainerImageManager {
|
|
if containerImageManager != nil {
|
|
return containerImageManager
|
|
}
|
|
containerImageManager = &SContainerImageManager{
|
|
SSharableVirtualResourceBaseManager: db.NewSharableVirtualResourceBaseManager(
|
|
SContainerImage{},
|
|
"container_images_tbl",
|
|
"container_image",
|
|
"container_images",
|
|
),
|
|
}
|
|
containerImageManager.SetVirtualObject(containerImageManager)
|
|
return containerImageManager
|
|
}
|
|
|
|
type SContainerImageManager struct {
|
|
db.SSharableVirtualResourceBaseManager
|
|
}
|
|
|
|
type SContainerImage struct {
|
|
db.SSharableVirtualResourceBase
|
|
|
|
ImageName string `width:"256" charset:"utf8" nullable:"false" list:"user" create:"required" update:"user"`
|
|
ImageLabel string `width:"128" charset:"utf8" nullable:"false" list:"user" create:"required" update:"user"`
|
|
CredentialId string `width:"128" charset:"utf8" nullable:"true" list:"user" create:"optional" update:"user"`
|
|
RegistryId string `width:"128" charset:"ascii" nullable:"true" list:"user" create:"optional" update:"user"`
|
|
}
|
|
|
|
func fetchImageCredential(ctx context.Context, userCred mcclient.TokenCredential, cid string) (*identityapi.CredentialDetails, error) {
|
|
s := auth.GetSession(ctx, userCred, options.Options.Region)
|
|
credJson, err := identity.Credentials.Get(s, cid, nil)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "Credentials.Get")
|
|
}
|
|
details := identityapi.CredentialDetails{}
|
|
err = credJson.Unmarshal(&details)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "Unmarshal")
|
|
}
|
|
return &details, nil
|
|
}
|
|
|
|
func normalizeRegistryHost(url string) string {
|
|
host := strings.TrimSpace(url)
|
|
host = strings.TrimPrefix(host, "https://")
|
|
host = strings.TrimPrefix(host, "http://")
|
|
return strings.TrimRight(host, "/")
|
|
}
|
|
|
|
func ensureImageNameMatchesRegistry(imageName string, reg *SContainerRegistry) error {
|
|
if reg == nil {
|
|
return nil
|
|
}
|
|
prefix := normalizeRegistryHost(reg.Url)
|
|
if prefix == "" {
|
|
return httperrors.NewInputParameterError("container registry %s has empty url", reg.GetId())
|
|
}
|
|
if imageName == prefix || strings.HasPrefix(imageName, prefix+"/") {
|
|
return nil
|
|
}
|
|
return httperrors.NewInputParameterError("image_name %q must start with registry prefix %q", imageName, prefix)
|
|
}
|
|
|
|
func (man *SContainerImageManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input *api.ContainerImageCreateInput) (*api.ContainerImageCreateInput, error) {
|
|
var err error
|
|
input.SharableVirtualResourceCreateInput, err = man.SSharableVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.SharableVirtualResourceCreateInput)
|
|
if err != nil {
|
|
return input, errors.Wrap(err, "validate SharableVirtualResourceCreateInput")
|
|
}
|
|
|
|
if input.ImageName == "" {
|
|
return input, httperrors.NewInputParameterError("image_name is required")
|
|
}
|
|
if input.ImageLabel == "" {
|
|
return input, httperrors.NewInputParameterError("image_label is required")
|
|
}
|
|
|
|
if len(input.CredentialId) > 0 {
|
|
cred, err := fetchImageCredential(ctx, userCred, input.CredentialId)
|
|
if err != nil {
|
|
return input, errors.Wrap(err, "fetchImageCredential")
|
|
}
|
|
input.CredentialId = cred.Id
|
|
}
|
|
|
|
if len(input.RegistryId) > 0 {
|
|
obj, err := GetContainerRegistryManager().FetchByIdOrName(ctx, userCred, input.RegistryId)
|
|
if err != nil {
|
|
return input, errors.Wrapf(err, "fetch container registry %s", input.RegistryId)
|
|
}
|
|
input.RegistryId = obj.GetId()
|
|
reg, ok := obj.(*SContainerRegistry)
|
|
if !ok {
|
|
return input, httperrors.NewGeneralError(errors.Errorf("invalid container registry object"))
|
|
}
|
|
if err := ensureImageNameMatchesRegistry(input.ImageName, reg); err != nil {
|
|
return input, err
|
|
}
|
|
if len(input.CredentialId) == 0 && reg.CredentialId != "" {
|
|
input.CredentialId = reg.CredentialId
|
|
}
|
|
}
|
|
|
|
input.Status = api.CONTAINER_IMAGE_STATUS_READY
|
|
return input, nil
|
|
}
|
|
|
|
func (man *SContainerImageManager) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input *api.ContainerImageUpdateInput) (*api.ContainerImageUpdateInput, error) {
|
|
var err error
|
|
input.SharableVirtualResourceCreateInput, err = man.SSharableVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.SharableVirtualResourceCreateInput)
|
|
if err != nil {
|
|
return input, errors.Wrap(err, "validate SharableVirtualResourceCreateInput")
|
|
}
|
|
|
|
if input.CredentialId != nil && len(*input.CredentialId) > 0 {
|
|
cred, err := fetchImageCredential(ctx, userCred, *input.CredentialId)
|
|
if err != nil {
|
|
return input, errors.Wrap(err, "fetchImageCredential")
|
|
}
|
|
input.CredentialId = &cred.Id
|
|
}
|
|
|
|
if input.RegistryId != nil && len(*input.RegistryId) > 0 {
|
|
obj, err := GetContainerRegistryManager().FetchByIdOrName(ctx, userCred, *input.RegistryId)
|
|
if err != nil {
|
|
return input, errors.Wrapf(err, "fetch container registry %s", *input.RegistryId)
|
|
}
|
|
id := obj.GetId()
|
|
input.RegistryId = &id
|
|
reg, ok := obj.(*SContainerRegistry)
|
|
if !ok {
|
|
return input, httperrors.NewGeneralError(errors.Errorf("invalid container registry object"))
|
|
}
|
|
if input.ImageName != nil {
|
|
if err := ensureImageNameMatchesRegistry(*input.ImageName, reg); err != nil {
|
|
return input, err
|
|
}
|
|
}
|
|
if input.CredentialId == nil || len(*input.CredentialId) == 0 {
|
|
if reg.CredentialId != "" {
|
|
input.CredentialId = ®.CredentialId
|
|
}
|
|
}
|
|
}
|
|
|
|
return input, nil
|
|
}
|
|
|
|
func (image *SContainerImage) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ContainerImageUpdateInput) (api.ContainerImageUpdateInput, error) {
|
|
if input.CredentialId != nil && len(*input.CredentialId) > 0 {
|
|
cred, err := fetchImageCredential(ctx, userCred, *input.CredentialId)
|
|
if err != nil {
|
|
return input, errors.Wrap(err, "fetchImageCredential")
|
|
}
|
|
input.CredentialId = &cred.Id
|
|
}
|
|
|
|
registryId := image.RegistryId
|
|
if input.RegistryId != nil {
|
|
registryId = *input.RegistryId
|
|
}
|
|
if len(registryId) > 0 {
|
|
obj, err := GetContainerRegistryManager().FetchByIdOrName(ctx, userCred, registryId)
|
|
if err != nil {
|
|
return input, errors.Wrapf(err, "fetch container registry %s", registryId)
|
|
}
|
|
id := obj.GetId()
|
|
if input.RegistryId != nil {
|
|
input.RegistryId = &id
|
|
}
|
|
reg, ok := obj.(*SContainerRegistry)
|
|
if !ok {
|
|
return input, httperrors.NewGeneralError(errors.Errorf("invalid container registry object"))
|
|
}
|
|
imageName := image.ImageName
|
|
if input.ImageName != nil {
|
|
imageName = *input.ImageName
|
|
}
|
|
if err := ensureImageNameMatchesRegistry(imageName, reg); err != nil {
|
|
return input, err
|
|
}
|
|
if input.CredentialId == nil || len(*input.CredentialId) == 0 {
|
|
if input.RegistryId != nil && reg.CredentialId != "" {
|
|
input.CredentialId = ®.CredentialId
|
|
}
|
|
}
|
|
}
|
|
return input, nil
|
|
}
|
|
|
|
func (man *SContainerImageManager) ListItemFilter(
|
|
ctx context.Context,
|
|
q *sqlchemy.SQuery,
|
|
userCred mcclient.TokenCredential,
|
|
input api.ContainerImageListInput,
|
|
) (*sqlchemy.SQuery, error) {
|
|
q, err := man.SSharableVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, input.SharableVirtualResourceListInput)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "SSharableBaseResourceManager.ListItemFilter")
|
|
}
|
|
if input.IsPublic != nil {
|
|
if *input.IsPublic {
|
|
q = q.IsTrue("is_public")
|
|
} else {
|
|
q = q.IsFalse("is_public")
|
|
}
|
|
}
|
|
if len(input.ImageLabel) > 0 {
|
|
q = q.Equals("image_label", input.ImageLabel)
|
|
}
|
|
if len(input.ImageName) > 0 {
|
|
q = q.Equals("image_name", input.ImageName)
|
|
}
|
|
if len(input.RegistryId) > 0 {
|
|
q = q.Equals("registry_id", input.RegistryId)
|
|
}
|
|
return q, nil
|
|
}
|
|
|
|
func (manager *SContainerImageManager) FetchCustomizeColumns(
|
|
ctx context.Context,
|
|
userCred mcclient.TokenCredential,
|
|
query jsonutils.JSONObject,
|
|
objs []interface{},
|
|
fields stringutils2.SSortedStrings,
|
|
isList bool,
|
|
) []api.ContainerImageDetails {
|
|
rows := make([]api.ContainerImageDetails, len(objs))
|
|
virtRows := manager.SSharableVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
|
|
|
|
registryIds := make([]string, 0)
|
|
for i := range objs {
|
|
image := objs[i].(*SContainerImage)
|
|
if image.RegistryId != "" {
|
|
registryIds = append(registryIds, image.RegistryId)
|
|
}
|
|
}
|
|
registryNames, err := db.FetchIdNameMap2(GetContainerRegistryManager(), registryIds)
|
|
if err != nil {
|
|
log.Errorf("FetchIdNameMap2 container_registries: %v", err)
|
|
}
|
|
|
|
for i := range rows {
|
|
rows[i] = api.ContainerImageDetails{
|
|
SharableVirtualResourceDetails: virtRows[i],
|
|
}
|
|
image := objs[i].(*SContainerImage)
|
|
if name, ok := registryNames[image.RegistryId]; ok {
|
|
rows[i].Registry = name
|
|
}
|
|
}
|
|
return rows
|
|
}
|
|
|
|
func (image *SContainerImage) ToContainerImage() string {
|
|
return fmt.Sprintf("%s:%s", image.ImageName, image.ImageLabel)
|
|
}
|