| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807 |
- // 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"
- "database/sql"
- "fmt"
- "net/http"
- "strconv"
- "strings"
- "time"
- "github.com/minio/minio-go/pkg/s3utils"
- "yunion.io/x/cloudmux/pkg/cloudprovider"
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/pkg/util/compare"
- "yunion.io/x/pkg/util/httputils"
- "yunion.io/x/pkg/util/rbacscope"
- "yunion.io/x/pkg/utils"
- "yunion.io/x/sqlchemy"
- api "yunion.io/x/onecloud/pkg/apis/compute"
- identity_apis "yunion.io/x/onecloud/pkg/apis/identity"
- "yunion.io/x/onecloud/pkg/appsrv"
- "yunion.io/x/onecloud/pkg/cloudcommon/db"
- "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
- "yunion.io/x/onecloud/pkg/cloudcommon/db/quotas"
- "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
- "yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
- "yunion.io/x/onecloud/pkg/compute/options"
- "yunion.io/x/onecloud/pkg/httperrors"
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/mcclient/auth"
- "yunion.io/x/onecloud/pkg/mcclient/modulebase"
- "yunion.io/x/onecloud/pkg/util/logclient"
- "yunion.io/x/onecloud/pkg/util/rbacutils"
- "yunion.io/x/onecloud/pkg/util/stringutils2"
- )
- type SBucketManager struct {
- db.SSharableVirtualResourceBaseManager
- db.SExternalizedResourceBaseManager
- SCloudregionResourceBaseManager
- SManagedResourceBaseManager
- }
- var BucketManager *SBucketManager
- func init() {
- BucketManager = &SBucketManager{
- SSharableVirtualResourceBaseManager: db.NewSharableVirtualResourceBaseManager(
- SBucket{},
- "buckets_tbl",
- "bucket",
- "buckets",
- ),
- }
- BucketManager.SetVirtualObject(BucketManager)
- }
- type SBucket struct {
- db.SSharableVirtualResourceBase
- db.SExternalizedResourceBase
- SCloudregionResourceBase `width:"36" charset:"ascii" nullable:"false" list:"user" create:"required"`
- SManagedResourceBase
- // CloudregionId string `width:"36" charset:"ascii" nullable:"false" list:"user" create:"required"`
- StorageClass string `width:"36" charset:"ascii" nullable:"false" list:"user"`
- Location string `width:"36" charset:"ascii" nullable:"false" list:"user"`
- Acl string `width:"36" charset:"ascii" nullable:"false" list:"user"`
- SizeBytes int64 `nullable:"false" default:"0" list:"user"`
- ObjectCnt int `nullable:"false" default:"0" list:"user"`
- SizeBytesLimit int64 `nullable:"false" default:"0" list:"user"`
- ObjectCntLimit int `nullable:"false" default:"0" list:"user"`
- AccessUrls jsonutils.JSONObject `nullable:"true" list:"user"`
- EnablePerfMon bool `default:"false" list:"user" update:"user" create:"optional"`
- }
- func (manager *SBucketManager) SetHandlerProcessTimeout(info *appsrv.SHandlerInfo, r *http.Request) time.Duration {
- if r.Method == http.MethodPost && strings.HasSuffix(r.URL.Path, "/upload") && r.Header.Get(api.BUCKET_UPLOAD_OBJECT_KEY_HEADER) != "" {
- log.Debugf("upload object, set process timeout to 2 hour!!!")
- return 2 * time.Hour
- }
- return manager.SSharableVirtualResourceBaseManager.SetHandlerProcessTimeout(info, r)
- }
- func (manager *SBucketManager) fetchBuckets(provider *SCloudprovider, region *SCloudregion) ([]SBucket, error) {
- q := manager.Query()
- if provider != nil {
- q = q.Equals("manager_id", provider.GetId())
- }
- if region != nil {
- q = q.Equals("cloudregion_id", region.GetId())
- }
- buckets := make([]SBucket, 0)
- err := db.FetchModelObjects(manager, q, &buckets)
- if err != nil && err != sql.ErrNoRows {
- return nil, errors.Wrap(err, "db.FetchModelObjects")
- }
- return buckets, nil
- }
- func (manager *SBucketManager) syncBuckets(ctx context.Context, userCred mcclient.TokenCredential, provider *SCloudprovider, region *SCloudregion, buckets []cloudprovider.ICloudBucket, xor bool) compare.SyncResult {
- lockman.LockRawObject(ctx, manager.Keyword(), fmt.Sprintf("%s-%s", provider.Id, region.Id))
- defer lockman.ReleaseRawObject(ctx, manager.Keyword(), fmt.Sprintf("%s-%s", provider.Id, region.Id))
- syncResult := compare.SyncResult{}
- dbBuckets, err := manager.fetchBuckets(provider, region)
- if err != nil {
- syncResult.Error(err)
- return syncResult
- }
- removed := make([]SBucket, 0)
- commondb := make([]SBucket, 0)
- commonext := make([]cloudprovider.ICloudBucket, 0)
- added := make([]cloudprovider.ICloudBucket, 0)
- err = compare.CompareSets(dbBuckets, buckets, &removed, &commondb, &commonext, &added)
- if err != nil {
- syncResult.Error(err)
- return syncResult
- }
- for i := 0; i < len(removed); i += 1 {
- err = removed[i].syncRemoveCloudBucket(ctx, userCred)
- if err != nil {
- syncResult.DeleteError(err)
- } else {
- syncResult.Delete()
- }
- }
- if !xor {
- for i := 0; i < len(commondb); i += 1 {
- err = commondb[i].SyncWithCloudBucket(ctx, userCred, commonext[i], false)
- if err != nil {
- syncResult.UpdateError(err)
- } else {
- syncResult.Update()
- }
- }
- }
- for i := 0; i < len(added); i += 1 {
- _, err := manager.newFromCloudBucket(ctx, userCred, added[i], provider, region)
- if err != nil {
- syncResult.AddError(err)
- } else {
- syncResult.Add()
- }
- }
- return syncResult
- }
- func (manager *SBucketManager) newFromCloudBucket(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- extBucket cloudprovider.ICloudBucket,
- provider *SCloudprovider,
- region *SCloudregion,
- ) (*SBucket, error) {
- bucket := SBucket{}
- bucket.SetModelManager(manager, &bucket)
- bucket.ExternalId = extBucket.GetGlobalId()
- bucket.ManagerId = provider.Id
- bucket.CloudregionId = region.Id
- bucket.Status = api.BUCKET_STATUS_READY
- created := extBucket.GetCreatedAt()
- if !created.IsZero() {
- bucket.CreatedAt = created
- }
- bucket.Location = extBucket.GetLocation()
- bucket.StorageClass = extBucket.GetStorageClass()
- bucket.Acl = string(extBucket.GetAcl())
- if !extBucket.GetCreatedAt().IsZero() {
- bucket.CreatedAt = extBucket.GetCreatedAt()
- }
- stats := extBucket.GetStats()
- bucket.SizeBytes = stats.SizeBytes
- if bucket.SizeBytes < 0 {
- bucket.SizeBytes = 0
- }
- bucket.ObjectCnt = stats.ObjectCount
- if bucket.ObjectCnt < 0 {
- bucket.ObjectCnt = 0
- }
- limit := extBucket.GetLimit()
- limitSupport := extBucket.LimitSupport()
- if limitSupport.SizeBytes > 0 {
- bucket.SizeBytesLimit = limit.SizeBytes
- }
- if limitSupport.ObjectCount > 0 {
- bucket.ObjectCntLimit = limit.ObjectCount
- }
- bucket.AccessUrls = jsonutils.Marshal(extBucket.GetAccessUrls())
- bucket.IsEmulated = false
- var err = func() error {
- lockman.LockRawObject(ctx, manager.Keyword(), "name")
- defer lockman.ReleaseRawObject(ctx, manager.Keyword(), "name")
- var err error
- bucket.Name, err = db.GenerateName(ctx, manager, nil, extBucket.GetName())
- if err != nil {
- return errors.Wrap(err, "db.GenerateName")
- }
- return manager.TableSpec().Insert(ctx, &bucket)
- }()
- if err != nil {
- return nil, err
- }
- SyncCloudProject(ctx, userCred, &bucket, provider.GetOwnerId(), extBucket, provider)
- notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
- Obj: &bucket,
- Action: notifyclient.ActionSyncCreate,
- })
- bucket.SyncShareState(ctx, userCred, provider.getAccountShareInfo())
- syncVirtualResourceMetadata(ctx, userCred, &bucket, extBucket, false)
- db.OpsLog.LogEvent(&bucket, db.ACT_CREATE, bucket.GetShortDesc(ctx), userCred)
- return &bucket, nil
- }
- func (bucket *SBucket) getStats() cloudprovider.SBucketStats {
- return cloudprovider.SBucketStats{
- SizeBytes: bucket.SizeBytes,
- ObjectCount: bucket.ObjectCnt,
- }
- }
- func (bucket *SBucket) GetShortDesc(ctx context.Context) *jsonutils.JSONDict {
- desc := bucket.SSharableVirtualResourceBase.GetShortDesc(ctx)
- desc.Add(jsonutils.NewInt(bucket.SizeBytesLimit), "size_bytes_limit")
- desc.Add(jsonutils.NewInt(bucket.SizeBytes), "size_bytes")
- desc.Add(jsonutils.NewInt(int64(bucket.ObjectCntLimit)), "object_cnt_limit")
- desc.Add(jsonutils.NewInt(int64(bucket.ObjectCnt)), "object_cnt")
- desc.Add(jsonutils.NewString(bucket.Acl), "acl")
- desc.Add(jsonutils.NewString(bucket.StorageClass), "storage_class")
- info := bucket.getCloudProviderInfo()
- desc.Update(jsonutils.Marshal(&info))
- return desc
- }
- func (bucket *SBucket) SyncWithCloudBucket(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- extBucket cloudprovider.ICloudBucket,
- statsOnly bool,
- ) error {
- oStats := bucket.getStats()
- diff, err := db.UpdateWithLock(ctx, bucket, func() error {
- stats := extBucket.GetStats()
- bucket.SizeBytes = stats.SizeBytes
- if bucket.SizeBytes < 0 {
- bucket.SizeBytes = 0
- }
- bucket.ObjectCnt = stats.ObjectCount
- if bucket.ObjectCnt < 0 {
- bucket.ObjectCnt = 0
- }
- created := extBucket.GetCreatedAt()
- if !created.IsZero() {
- bucket.CreatedAt = created
- }
- if !statsOnly {
- limit := extBucket.GetLimit()
- limitSupport := extBucket.LimitSupport()
- if limitSupport.SizeBytes > 0 {
- bucket.SizeBytesLimit = limit.SizeBytes
- }
- if limitSupport.ObjectCount > 0 {
- bucket.ObjectCntLimit = limit.ObjectCount
- }
- bucket.Acl = string(extBucket.GetAcl())
- bucket.Location = extBucket.GetLocation()
- bucket.StorageClass = extBucket.GetStorageClass()
- bucket.AccessUrls = jsonutils.Marshal(extBucket.GetAccessUrls())
- bucket.Status = api.BUCKET_STATUS_READY
- }
- return nil
- })
- if err != nil {
- return errors.Wrap(err, "db.UpdateWithLock")
- }
- if account := bucket.GetCloudaccount(); account != nil {
- syncVirtualResourceMetadata(ctx, userCred, bucket, extBucket, account.ReadOnly)
- }
- db.OpsLog.LogSyncUpdate(bucket, diff, userCred)
- if len(diff) > 0 {
- notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
- Obj: bucket,
- Action: notifyclient.ActionSyncUpdate,
- })
- }
- if !oStats.Equals(extBucket.GetStats()) {
- db.OpsLog.LogEvent(bucket, api.BUCKET_OPS_STATS_CHANGE, bucket.GetShortDesc(ctx), userCred)
- }
- provider := bucket.GetCloudprovider()
- if provider != nil {
- SyncCloudProject(ctx, userCred, bucket, provider.GetOwnerId(), extBucket, provider)
- bucket.SyncShareState(ctx, userCred, provider.getAccountShareInfo())
- }
- return nil
- }
- func (bucket *SBucket) syncRemoveCloudBucket(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- ) error {
- lockman.LockObject(ctx, bucket)
- defer lockman.ReleaseObject(ctx, bucket)
- err := bucket.RealDelete(ctx, userCred)
- if err != nil {
- return errors.Wrap(err, "RealDelete")
- }
- notifyclient.EventNotify(ctx, userCred, notifyclient.SEventNotifyParam{
- Obj: bucket,
- Action: notifyclient.ActionSyncDelete,
- })
- return nil
- }
- func (bucket *SBucket) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
- // override
- log.Infof("bucket delete do nothing")
- return nil
- }
- func (bucket *SBucket) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
- return bucket.SSharableVirtualResourceBase.Delete(ctx, userCred)
- }
- func (bucket *SBucket) RemoteDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
- if len(bucket.ExternalId) > 0 {
- iregion, err := bucket.GetIRegion(ctx)
- if err != nil {
- return errors.Wrap(err, "bucket.GetIRegion")
- }
- err = iregion.DeleteIBucket(bucket.ExternalId)
- if err != nil {
- return errors.Wrap(err, "iregion.DeleteIBucket")
- }
- }
- err := bucket.RealDelete(ctx, userCred)
- if err != nil {
- return errors.Wrap(err, "bucket.RealDelete")
- }
- return nil
- }
- func (bucket *SBucket) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
- return bucket.StartBucketDeleteTask(ctx, userCred, "")
- }
- func (bucket *SBucket) StartBucketDeleteTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
- params := jsonutils.NewDict()
- task, err := taskman.TaskManager.NewTask(ctx, "BucketDeleteTask", bucket, userCred, params, parentTaskId, "", nil)
- if err != nil {
- log.Errorf("%s", err)
- return err
- }
- bucket.SetStatus(ctx, userCred, api.CLOUD_PROVIDER_START_DELETE, "StartBucketDeleteTask")
- task.ScheduleRun(nil)
- return nil
- }
- func (bucket *SBucket) GetIRegion(ctx context.Context) (cloudprovider.ICloudRegion, error) {
- provider, err := bucket.GetDriver(ctx)
- if err != nil {
- return nil, err
- }
- if provider.GetFactory().IsOnPremise() {
- return provider.GetOnPremiseIRegion()
- } else {
- region, err := bucket.GetRegion()
- if err != nil {
- return nil, errors.Wrap(err, "bucket.GetRegion")
- }
- return provider.GetIRegionById(region.GetExternalId())
- }
- }
- func (bucket *SBucket) GetIBucket(ctx context.Context) (cloudprovider.ICloudBucket, error) {
- iregion, err := bucket.GetIRegion(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "bucket.GetIRegion")
- }
- return iregion.GetIBucketById(bucket.ExternalId)
- }
- func isValidBucketName(name string) error {
- return s3utils.CheckValidBucketNameStrict(name)
- }
- func (manager *SBucketManager) ValidateCreateData(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- ownerId mcclient.IIdentityProvider,
- query jsonutils.JSONObject,
- input api.BucketCreateInput,
- ) (api.BucketCreateInput, error) {
- var err error
- var cloudRegionV *SCloudregion
- cloudRegionV, input.CloudregionResourceInput, err = ValidateCloudregionResourceInput(ctx, userCred, input.CloudregionResourceInput)
- if err != nil {
- return input, errors.Wrap(err, "ValidateCloudregionResourceInput")
- }
- var managerV *SCloudprovider
- managerV, input.CloudproviderResourceInput, err = ValidateCloudproviderResourceInput(ctx, userCred, input.CloudproviderResourceInput)
- if err != nil {
- return input, errors.Wrap(err, "ValidateCloudproviderResourceInput")
- }
- if len(input.Name) == 0 {
- return input, httperrors.NewInputParameterError("missing name")
- }
- err = isValidBucketName(input.Name)
- if err != nil {
- return input, httperrors.NewInputParameterError("invalid bucket name %s: %s", input.Name, err)
- }
- if len(input.StorageClass) > 0 {
- driver, err := managerV.GetProvider(ctx)
- if err != nil {
- return input, errors.Wrap(err, "GetProvider")
- }
- if !utils.IsInStringArray(input.StorageClass, driver.GetStorageClasses(cloudRegionV.Id)) {
- return input, errors.Wrapf(httperrors.ErrInputParameter, "invalid storage class %s", input.StorageClass)
- }
- }
- quotaKeys := fetchRegionalQuotaKeys(rbacscope.ScopeProject, ownerId, cloudRegionV, managerV)
- pendingUsage := SRegionQuota{Bucket: 1}
- pendingUsage.SetKeys(quotaKeys)
- if err := quotas.CheckSetPendingQuota(ctx, userCred, &pendingUsage); err != nil {
- return input, httperrors.NewOutOfQuotaError("%s", err)
- }
- input.SharableVirtualResourceCreateInput, err = manager.SSharableVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.SharableVirtualResourceCreateInput)
- if err != nil {
- return input, errors.Wrap(err, "SSharableVirtualResourceBaseManager.ValidateCreateData")
- }
- return input, nil
- }
- func (bucket *SBucket) GetQuotaKeys() (quotas.IQuotaKeys, error) {
- region, _ := bucket.GetRegion()
- if region == nil {
- return nil, errors.Wrap(httperrors.ErrInvalidStatus, "no valid region")
- }
- return fetchRegionalQuotaKeys(
- rbacscope.ScopeProject,
- bucket.GetOwnerId(),
- region,
- bucket.GetCloudprovider(),
- ), nil
- }
- func (bucket *SBucket) PostCreate(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- ownerId mcclient.IIdentityProvider,
- query jsonutils.JSONObject,
- data jsonutils.JSONObject,
- ) {
- bucket.SSharableVirtualResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
- pendingUsage := SRegionQuota{Bucket: 1}
- keys, err := bucket.GetQuotaKeys()
- if err != nil {
- log.Errorf("bucket.GetQuotaKeys fail %s", err)
- } else {
- pendingUsage.SetKeys(keys)
- err = quotas.CancelPendingUsage(ctx, userCred, &pendingUsage, &pendingUsage, true)
- if err != nil {
- log.Errorf("CancelPendingUsage error %s", err)
- }
- }
- bucket.SetStatus(ctx, userCred, api.BUCKET_STATUS_START_CREATE, "PostCreate")
- task, err := taskman.TaskManager.NewTask(ctx, "BucketCreateTask", bucket, userCred, nil, "", "", nil)
- if err != nil {
- bucket.SetStatus(ctx, userCred, api.BUCKET_STATUS_CREATE_FAIL, errors.Wrapf(err, "NewTask").Error())
- return
- }
- task.ScheduleRun(nil)
- }
- func (bucket *SBucket) ValidateUpdateData(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- input api.BucketUpdateInput,
- ) (api.BucketUpdateInput, error) {
- var err error
- if len(input.Name) > 0 {
- err := isValidBucketName(input.Name)
- if err != nil {
- return input, httperrors.NewInputParameterError("invalid bucket name(%s): %s", input.Name, err)
- }
- }
- input.SharableVirtualResourceBaseUpdateInput, err = bucket.SSharableVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input.SharableVirtualResourceBaseUpdateInput)
- if err != nil {
- return input, errors.Wrap(err, "SSharableVirtualResourceBase.ValidateUpdateData")
- }
- return input, nil
- }
- func (bucket *SBucket) RemoteCreate(ctx context.Context, userCred mcclient.TokenCredential) error {
- iregion, err := bucket.GetIRegion(ctx)
- if err != nil {
- return errors.Wrap(err, "bucket.GetIRegion")
- }
- err = iregion.CreateIBucket(bucket.Name, bucket.StorageClass, bucket.Acl)
- if err != nil {
- return errors.Wrap(err, "iregion.CreateIBucket")
- }
- extBucket, err := iregion.GetIBucketByName(bucket.Name)
- if err != nil {
- return errors.Wrap(err, "iregion.GetIBucketByName")
- }
- err = db.SetExternalId(bucket, userCred, extBucket.GetGlobalId())
- if err != nil {
- return errors.Wrap(err, "db.SetExternalId")
- }
- tags, _ := bucket.GetAllUserMetadata()
- if len(tags) > 0 {
- _, err = cloudprovider.SetBucketTags(ctx, extBucket, bucket.ManagerId, tags)
- if err != nil {
- logclient.AddSimpleActionLog(bucket, logclient.ACT_UPDATE_TAGS, err, userCred, false)
- }
- }
- err = bucket.SyncWithCloudBucket(ctx, userCred, extBucket, false)
- if err != nil {
- return errors.Wrap(err, "SyncWithCloudBucket")
- }
- return nil
- }
- func (manager *SBucketManager) FetchCustomizeColumns(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- objs []interface{},
- fields stringutils2.SSortedStrings,
- isList bool,
- ) []api.BucketDetails {
- rows := make([]api.BucketDetails, len(objs))
- virtRows := manager.SSharableVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
- managerRows := manager.SManagedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
- regionRows := manager.SCloudregionResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
- for i := range rows {
- rows[i] = api.BucketDetails{
- SharableVirtualResourceDetails: virtRows[i],
- ManagedResourceInfo: managerRows[i],
- CloudregionResourceInfo: regionRows[i],
- }
- rows[i] = objs[i].(*SBucket).getMoreDetails(rows[i])
- }
- return rows
- }
- func joinPath(ep, path string) string {
- return strings.TrimRight(ep, "/") + "/" + strings.TrimLeft(path, "/")
- }
- func (bucket *SBucket) getMoreDetails(out api.BucketDetails) api.BucketDetails {
- s3gwUrl, _ := auth.GetServiceURL("s3gateway", options.Options.Region, "", identity_apis.EndpointInterfacePublic, httputils.POST)
- if len(s3gwUrl) > 0 {
- accessUrls := make([]cloudprovider.SBucketAccessUrl, 0)
- if bucket.AccessUrls != nil {
- err := bucket.AccessUrls.Unmarshal(&accessUrls)
- if err != nil {
- log.Errorf("bucket.AccessUrls.Unmarshal fail %s", err)
- }
- }
- find := false
- for i := range accessUrls {
- if strings.HasPrefix(accessUrls[i].Url, s3gwUrl) {
- find = true
- break
- }
- }
- if !find {
- accessUrls = append(accessUrls, cloudprovider.SBucketAccessUrl{
- Url: joinPath(s3gwUrl, bucket.Name),
- Description: "s3gateway",
- })
- out.AccessUrls = accessUrls
- }
- }
- return out
- }
- func (bucket *SBucket) getCloudProviderInfo() SCloudProviderInfo {
- region, _ := bucket.GetRegion()
- provider := bucket.GetCloudprovider()
- return MakeCloudProviderInfo(region, nil, provider)
- }
- // 对象存储的存储桶列表
- func (manager *SBucketManager) ListItemFilter(
- ctx context.Context,
- q *sqlchemy.SQuery,
- userCred mcclient.TokenCredential,
- query api.BucketListInput,
- ) (*sqlchemy.SQuery, error) {
- var err error
- q, err = manager.SCloudregionResourceBaseManager.ListItemFilter(ctx, q, userCred, query.RegionalFilterListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SCloudregionResourceBaseManager.ListItemFilter")
- }
- q, err = manager.SExternalizedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ExternalizedResourceBaseListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SExternalizedResourceBaseManager.ListItemFilter")
- }
- q, err = manager.SManagedResourceBaseManager.ListItemFilter(ctx, q, userCred, query.ManagedResourceListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SManagedResourceBaseManager.ListItemFilter")
- }
- q, err = manager.SSharableVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, query.SharableVirtualResourceListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SSharableVirtualResourceBaseManager.ListItemFilter")
- }
- if len(query.StorageClass) > 0 {
- q = q.In("storage_class", query.StorageClass)
- }
- if len(query.Location) > 0 {
- q = q.In("location", query.Location)
- }
- if len(query.Acl) > 0 {
- q = q.In("acl", query.Acl)
- }
- return q, nil
- }
- func (manager *SBucketManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
- var err error
- q, err = manager.SSharableVirtualResourceBaseManager.QueryDistinctExtraField(q, field)
- if err == nil {
- return q, nil
- }
- q, err = manager.SManagedResourceBaseManager.QueryDistinctExtraField(q, field)
- if err == nil {
- return q, nil
- }
- q, err = manager.SCloudregionResourceBaseManager.QueryDistinctExtraField(q, field)
- if err == nil {
- return q, nil
- }
- return q, httperrors.ErrNotFound
- }
- func (manager *SBucketManager) QueryDistinctExtraFields(q *sqlchemy.SQuery, resource string, fields []string) (*sqlchemy.SQuery, error) {
- var err error
- q, err = manager.SManagedResourceBaseManager.QueryDistinctExtraFields(q, resource, fields)
- if err == nil {
- return q, nil
- }
- return q, httperrors.ErrNotFound
- }
- func (manager *SBucketManager) OrderByExtraFields(
- ctx context.Context,
- q *sqlchemy.SQuery,
- userCred mcclient.TokenCredential,
- query api.BucketListInput,
- ) (*sqlchemy.SQuery, error) {
- var err error
- q, err = manager.SCloudregionResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.RegionalFilterListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SCloudregionResourceBaseManager.OrderByExtraFields")
- }
- q, err = manager.SManagedResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.ManagedResourceListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SManagedResourceBaseManager.OrderByExtraFields")
- }
- q, err = manager.SSharableVirtualResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.SharableVirtualResourceListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SSharableVirtualResourceBaseManager.OrderByExtraFields")
- }
- return q, nil
- }
- // 获取bucket的对象列表
- //
- // 获取bucket的对象列表
- func (bucket *SBucket) GetDetailsObjects(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- input api.BucketGetObjectsInput,
- ) (api.BucketGetObjectsOutput, error) {
- output := api.BucketGetObjectsOutput{}
- if len(bucket.ExternalId) == 0 {
- return output, httperrors.NewInvalidStatusError("no external bucket")
- }
- iBucket, err := bucket.GetIBucket(ctx)
- if err != nil {
- return output, errors.Wrap(err, "GetIBucket")
- }
- prefix := input.Prefix
- isRecursive := false
- if input.Recursive != nil {
- isRecursive = *input.Recursive
- }
- marker := input.PagingMarker
- limit := 0
- if input.Limit != nil {
- limit = *input.Limit
- }
- if limit <= 0 {
- limit = 50
- } else if limit > 1000 {
- limit = 1000
- }
- objects, nextMarker, err := cloudprovider.GetPagedObjects(iBucket, prefix, isRecursive, marker, int(limit))
- if err != nil {
- return output, err
- }
- for i := range objects {
- output.Data = append(output.Data, cloudprovider.ICloudObject2Struct(objects[i]))
- }
- output.MarkerField = "key"
- output.MarkerOrder = "DESC"
- if len(nextMarker) > 0 {
- output.NextMarker = nextMarker
- }
- return output, nil
- }
- // 获取访问对象的临时URL
- func (bucket *SBucket) PerformTempUrl(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- input api.BucketPerformTempUrlInput,
- ) (api.BucketPerformTempUrlOutput, error) {
- output := api.BucketPerformTempUrlOutput{}
- if len(bucket.ExternalId) == 0 {
- return output, httperrors.NewInvalidStatusError("no external bucket")
- }
- method := input.Method
- key := input.Key
- expire := 0
- if input.ExpireSeconds != nil {
- expire = *input.ExpireSeconds
- }
- if len(method) == 0 {
- method = "GET"
- }
- if len(key) == 0 {
- return output, httperrors.NewInputParameterError("missing key")
- }
- if expire == 0 {
- expire = 60 // default 60 seconds
- }
- iBucket, err := bucket.GetIBucket(ctx)
- if err != nil {
- return output, errors.Wrap(err, "GetIBucket")
- }
- tmpUrl, err := iBucket.GetTempUrl(method, key, time.Duration(expire)*time.Second)
- if err != nil {
- return output, httperrors.NewInternalServerError("fail to generate temp url: %s", err)
- }
- output.Url = tmpUrl
- return output, nil
- }
- // 新建对象目录
- func (bucket *SBucket) PerformMakedir(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- input api.BucketPerformMakedirInput,
- ) (jsonutils.JSONObject, error) {
- if len(bucket.ExternalId) == 0 {
- return nil, httperrors.NewInvalidStatusError("no external bucket")
- }
- key := input.Key
- key = strings.Trim(key, " /")
- if len(key) == 0 {
- return nil, httperrors.NewInputParameterError("empty directory name")
- }
- err := s3utils.CheckValidObjectName(key)
- if err != nil {
- return nil, httperrors.NewInputParameterError("invalid key %s: %s", key, err)
- }
- iBucket, err := bucket.GetIBucket(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "GetIBucket")
- }
- _, err = cloudprovider.GetIObject(iBucket, key+"/")
- if err == nil {
- // replace
- return nil, nil
- } else if err != cloudprovider.ErrNotFound {
- return nil, httperrors.NewInternalServerError("GetIObject fail %s", err)
- }
- if bucket.ObjectCntLimit > 0 && bucket.ObjectCntLimit < bucket.ObjectCnt+1 {
- return nil, httperrors.NewOutOfQuotaError("object count limit exceeds")
- }
- pendingUsage := SRegionQuota{ObjectGB: 0, ObjectCnt: 1}
- keys, err := bucket.GetQuotaKeys()
- if err != nil {
- return nil, httperrors.NewInternalServerError("bucket.GetQuotaKeys %s", err)
- }
- pendingUsage.SetKeys(keys)
- if err := quotas.CheckSetPendingQuota(ctx, userCred, &pendingUsage); err != nil {
- return nil, httperrors.NewOutOfQuotaError("%s", err)
- }
- err = cloudprovider.Makedir(ctx, iBucket, key+"/")
- if err != nil {
- return nil, httperrors.NewInternalServerError("fail to mkdir: %s", err)
- }
- db.OpsLog.LogEvent(bucket, db.ACT_MKDIR, key, userCred)
- logclient.AddActionLogWithContext(ctx, bucket, logclient.ACT_MKDIR, key, userCred, true)
- bucket.SyncWithCloudBucket(ctx, userCred, iBucket, true)
- quotas.CancelPendingUsage(ctx, userCred, &pendingUsage, &pendingUsage, true)
- return nil, nil
- }
- // 删除对象
- //
- // 删除对象
- func (bucket *SBucket) PerformDelete(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- input api.BucketPerformDeleteInput,
- ) (jsonutils.JSONObject, error) {
- if len(bucket.ExternalId) == 0 {
- return nil, httperrors.NewInvalidStatusError("no external bucket")
- }
- keyStrs := input.Keys
- if len(keyStrs) == 0 {
- return nil, httperrors.NewInputParameterError("empty keys")
- }
- iBucket, err := bucket.GetIBucket(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "GetIBucket")
- }
- ok := jsonutils.NewDict()
- results := modulebase.BatchDo(keyStrs, func(key string) (jsonutils.JSONObject, error) {
- if strings.HasSuffix(key, "/") {
- err = cloudprovider.DeletePrefix(ctx, iBucket, key)
- } else {
- err = iBucket.DeleteObject(ctx, key)
- }
- if err != nil {
- return nil, errors.Wrap(err, "DeletePrefix")
- } else {
- return ok, nil
- }
- })
- db.OpsLog.LogEvent(bucket, db.ACT_DELETE_OBJECT, keyStrs, userCred)
- logclient.AddActionLogWithContext(ctx, bucket, logclient.ACT_DELETE_OBJECT, keyStrs, userCred, true)
- bucket.SyncWithCloudBucket(ctx, userCred, iBucket, true)
- return modulebase.SubmitResults2JSON(results), nil
- }
- // 上传对象
- //
- // 上传对象
- func (bucket *SBucket) PerformUpload(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- data jsonutils.JSONObject,
- ) (jsonutils.JSONObject, error) {
- if len(bucket.ExternalId) == 0 {
- return nil, httperrors.NewInvalidStatusError("no external bucket")
- }
- appParams := appsrv.AppContextGetParams(ctx)
- key := appParams.Request.Header.Get(api.BUCKET_UPLOAD_OBJECT_KEY_HEADER)
- if strings.HasSuffix(key, "/") {
- return nil, httperrors.NewInputParameterError("object key should not ends with /")
- }
- err := s3utils.CheckValidObjectName(key)
- if err != nil {
- return nil, httperrors.NewInputParameterError("invalid object key: %s", err)
- }
- iBucket, err := bucket.GetIBucket(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "GetIBucket")
- }
- meta := cloudprovider.FetchMetaFromHttpHeader(cloudprovider.META_HEADER_PREFIX, appParams.Request.Header)
- sizeStr := appParams.Request.Header.Get("Content-Length")
- if len(sizeStr) == 0 {
- return nil, httperrors.NewInputParameterError("missing Content-Length")
- }
- sizeBytes, err := strconv.ParseInt(sizeStr, 10, 64)
- if err != nil {
- return nil, httperrors.NewInputParameterError("Illegal Content-Length %s", sizeStr)
- }
- if sizeBytes < 0 {
- return nil, httperrors.NewInputParameterError("Content-Length negative %d", sizeBytes)
- }
- storageClass := appParams.Request.Header.Get(api.BUCKET_UPLOAD_OBJECT_STORAGECLASS_HEADER)
- driver, err := bucket.GetDriver(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "GetDriver")
- }
- if len(storageClass) > 0 && !utils.IsInStringArray(storageClass, driver.GetStorageClasses(bucket.CloudregionId)) {
- return nil, errors.Wrapf(httperrors.ErrInputParameter, "invalid storage class %s", storageClass)
- }
- aclStr := appParams.Request.Header.Get(api.BUCKET_UPLOAD_OBJECT_ACL_HEADER)
- if len(aclStr) > 0 && !utils.IsInStringArray(aclStr, driver.GetObjectCannedAcls(bucket.CloudregionId)) {
- return nil, errors.Wrapf(httperrors.ErrInputParameter, "invalid acl %s", aclStr)
- }
- inc := cloudprovider.SBucketStats{}
- obj, err := cloudprovider.GetIObject(iBucket, key)
- if err == nil {
- // replace
- inc.SizeBytes = sizeBytes - obj.GetSizeBytes()
- if inc.SizeBytes < 0 {
- inc.SizeBytes = 0
- }
- } else if errors.Cause(err) == cloudprovider.ErrNotFound {
- // new upload
- inc.SizeBytes = sizeBytes
- inc.ObjectCount = 1
- } else {
- return nil, httperrors.NewInternalServerError("GetIObject error %s", err)
- }
- if bucket.SizeBytesLimit > 0 && inc.SizeBytes > 0 && bucket.SizeBytesLimit < bucket.SizeBytes+inc.SizeBytes {
- return nil, httperrors.NewOutOfQuotaError("object size limit exceeds")
- }
- if bucket.ObjectCntLimit > 0 && inc.ObjectCount > 0 && bucket.ObjectCntLimit < bucket.ObjectCnt+inc.ObjectCount {
- return nil, httperrors.NewOutOfQuotaError("object count limit exceeds")
- }
- pendingUsage := SRegionQuota{ObjectGB: int(inc.SizeBytes / 1000 / 1000 / 1000), ObjectCnt: inc.ObjectCount}
- keys, err := bucket.GetQuotaKeys()
- if err != nil {
- return nil, httperrors.NewInternalServerError("bucket.GetQuotaKeys fail %s", err)
- }
- pendingUsage.SetKeys(keys)
- if !pendingUsage.IsEmpty() {
- if err := quotas.CheckSetPendingQuota(ctx, userCred, &pendingUsage); err != nil {
- return nil, httperrors.NewOutOfQuotaError("%s", err)
- }
- }
- err = cloudprovider.UploadObject(ctx, iBucket, key, 0, appParams.Request.Body, sizeBytes, cloudprovider.TBucketACLType(aclStr), storageClass, meta, false)
- if err != nil {
- if !pendingUsage.IsEmpty() {
- quotas.CancelPendingUsage(ctx, userCred, &pendingUsage, &pendingUsage, false)
- }
- return nil, httperrors.NewInternalServerError("put object error %s", err)
- }
- db.OpsLog.LogEvent(bucket, db.ACT_UPLOAD_OBJECT, key, userCred)
- logclient.AddActionLogWithContext(ctx, bucket, logclient.ACT_UPLOAD_OBJECT, key, userCred, true)
- bucket.SyncWithCloudBucket(ctx, userCred, iBucket, true)
- if !pendingUsage.IsEmpty() {
- quotas.CancelPendingUsage(ctx, userCred, &pendingUsage, &pendingUsage, true)
- }
- return nil, nil
- }
- // 设置对象和bucket的ACL
- //
- // 设置对象和bucket的ACL
- func (bucket *SBucket) PerformAcl(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- input api.BucketAclInput,
- ) (jsonutils.JSONObject, error) {
- err := input.Validate()
- if err != nil {
- return nil, errors.Wrap(err, "ValidateInput")
- }
- provider, err := bucket.GetDriver(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "GetDriver")
- }
- iBucket, objects, err := bucket.processObjectsActionInput(ctx, input.BucketObjectsActionInput)
- if err != nil {
- return nil, errors.Wrap(err, "processObjectsActionInput")
- }
- if len(objects) == 0 {
- if !utils.IsInStringArray(string(input.Acl), provider.GetBucketCannedAcls(bucket.CloudregionId)) {
- return nil, errors.Wrapf(httperrors.ErrInputParameter, "unsupported bucket canned acl %s", input.Acl)
- }
- err = iBucket.SetAcl(input.Acl)
- if err != nil {
- return nil, httperrors.NewInternalServerError("setAcl error %s", err)
- }
- err = bucket.SyncWithCloudBucket(ctx, userCred, iBucket, false)
- if err != nil {
- return nil, httperrors.NewInternalServerError("syncWithCloudBucket error %s", err)
- }
- return nil, nil
- }
- if !utils.IsInStringArray(string(input.Acl), provider.GetObjectCannedAcls(bucket.CloudregionId)) {
- return nil, errors.Wrapf(httperrors.ErrInputParameter, "unsupported object canned acl %s", input.Acl)
- }
- errs := make([]error, 0)
- for _, object := range objects {
- err := object.SetAcl(input.Acl)
- if err != nil {
- errs = append(errs, errors.Wrap(err, object.GetKey()))
- }
- }
- if len(errs) > 0 {
- return nil, errors.NewAggregate(errs)
- } else {
- return nil, nil
- }
- }
- // 同步存储桶状态
- //
- // 同步存储桶状态
- func (bucket *SBucket) PerformSyncstatus(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- input api.BucketSyncstatusInput,
- ) (jsonutils.JSONObject, error) {
- var openTask = true
- count, err := taskman.TaskManager.QueryTasksOfObject(bucket, time.Now().Add(-3*time.Minute), &openTask).CountWithError()
- if err != nil {
- return nil, err
- }
- if count > 0 {
- return nil, httperrors.NewBadRequestError("Bucket has %d task active, can't sync status", count)
- }
- return nil, StartResourceSyncStatusTask(ctx, userCred, bucket, "BucketSyncstatusTask", "")
- }
- func (bucket *SBucket) PerformSync(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- data jsonutils.JSONObject,
- ) (jsonutils.JSONObject, error) {
- if len(bucket.ExternalId) == 0 {
- return nil, httperrors.NewInvalidStatusError("no external bucket")
- }
- statsOnly := jsonutils.QueryBoolean(data, "stats_only", false)
- iBucket, err := bucket.GetIBucket(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "GetIBucket")
- }
- err = bucket.SyncWithCloudBucket(ctx, userCred, iBucket, statsOnly)
- if err != nil {
- return nil, httperrors.NewInternalServerError("syncWithCloudBucket error %s", err)
- }
- return nil, nil
- }
- func (bucket *SBucket) ValidateDeleteCondition(ctx context.Context, info jsonutils.JSONObject) error {
- if bucket.Status == api.BUCKET_STATUS_UNKNOWN {
- return bucket.SSharableVirtualResourceBase.ValidateDeleteCondition(ctx, nil)
- }
- if bucket.ObjectCnt > 0 {
- return httperrors.NewNotEmptyError("Buckets that are not empty do not support this operation")
- }
- return bucket.SSharableVirtualResourceBase.ValidateDeleteCondition(ctx, info)
- }
- // 获取对象或bucket的ACL
- //
- // 获取对象或bucket的ACL
- func (bucket *SBucket) GetDetailsAcl(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- input api.BucketGetAclInput,
- ) (api.BucketGetAclOutput, error) {
- output := api.BucketGetAclOutput{}
- if len(bucket.ExternalId) == 0 {
- return output, httperrors.NewInvalidStatusError("no external bucket")
- }
- iBucket, err := bucket.GetIBucket(ctx)
- if err != nil {
- return output, errors.Wrap(err, "GetIBucket")
- }
- objKey := input.Key
- var acl cloudprovider.TBucketACLType
- if len(objKey) == 0 {
- acl = iBucket.GetAcl()
- } else {
- object, err := cloudprovider.GetIObject(iBucket, objKey)
- if err != nil {
- if errors.Cause(err) == cloudprovider.ErrNotFound {
- return output, httperrors.NewNotFoundError("object %s not found", objKey)
- } else {
- return output, httperrors.NewInternalServerError("iBucket.GetIObjects error %s", err)
- }
- }
- acl = object.GetAcl()
- }
- output.Acl = string(acl)
- return output, nil
- }
- func (bucket *SBucket) PerformSetWebsite(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- input api.BucketWebsiteConf,
- ) (jsonutils.JSONObject, error) {
- err := input.Validate()
- if err != nil {
- return nil, err
- }
- iBucket, err := bucket.GetIBucket(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "GetIBucket")
- }
- bucketWebsiteConf := cloudprovider.SBucketWebsiteConf{
- Index: input.Index,
- ErrorDocument: input.ErrorDocument,
- Protocol: input.Protocol,
- }
- for i := range input.Rules {
- bucketWebsiteConf.Rules = append(bucketWebsiteConf.Rules, cloudprovider.SBucketWebsiteRoutingRule{
- ConditionErrorCode: input.Rules[i].ConditionErrorCode,
- ConditionPrefix: input.Rules[i].ConditionPrefix,
- RedirectProtocol: input.Rules[i].RedirectProtocol,
- RedirectReplaceKey: input.Rules[i].RedirectReplaceKey,
- RedirectReplaceKeyPrefix: input.Rules[i].RedirectReplaceKeyPrefix,
- })
- }
- err = iBucket.SetWebsite(bucketWebsiteConf)
- if err != nil {
- return nil, httperrors.NewInternalServerError("iBucket.SetWebsite error %s", err)
- }
- db.OpsLog.LogEvent(bucket, db.ACT_SET_WEBSITE, bucketWebsiteConf, userCred)
- logclient.AddActionLogWithContext(ctx, bucket, logclient.ACT_SET_WEBSITE, bucketWebsiteConf, userCred, true)
- return nil, nil
- }
- func (bucket *SBucket) PerformDeleteWebsite(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- input jsonutils.JSONObject,
- ) (jsonutils.JSONObject, error) {
- iBucket, err := bucket.GetIBucket(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "GetIBucket")
- }
- err = iBucket.DeleteWebSiteConf()
- if err != nil {
- return nil, httperrors.NewInternalServerError("iBucket.DeleteWebSiteConf error %s", err)
- }
- db.OpsLog.LogEvent(bucket, db.ACT_DELETE_WEBSITE, "", userCred)
- logclient.AddActionLogWithContext(ctx, bucket, logclient.ACT_DELETE_WEBSITE, "", userCred, true)
- return nil, nil
- }
- func (bucket *SBucket) GetDetailsWebsite(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- input jsonutils.JSONObject,
- ) (api.BucketWebsiteConf, error) {
- websiteConf := api.BucketWebsiteConf{}
- iBucket, err := bucket.GetIBucket(ctx)
- if err != nil {
- return websiteConf, errors.Wrap(err, "GetIBucket")
- }
- conf, err := iBucket.GetWebsiteConf()
- if err != nil {
- return websiteConf, httperrors.NewInternalServerError("iBucket.GetWebsiteConf error %s", err)
- }
- websiteConf.Index = conf.Index
- websiteConf.ErrorDocument = conf.ErrorDocument
- websiteConf.Protocol = conf.Protocol
- websiteConf.Url = conf.Url
- for i := range conf.Rules {
- websiteConf.Rules = append(websiteConf.Rules, api.BucketWebsiteRoutingRule{
- ConditionErrorCode: conf.Rules[i].ConditionErrorCode,
- ConditionPrefix: conf.Rules[i].ConditionPrefix,
- RedirectProtocol: conf.Rules[i].RedirectProtocol,
- RedirectReplaceKey: conf.Rules[i].RedirectReplaceKey,
- RedirectReplaceKeyPrefix: conf.Rules[i].RedirectReplaceKeyPrefix,
- })
- }
- return websiteConf, nil
- }
- func (bucket *SBucket) PerformSetCors(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- input api.BucketCORSRules,
- ) (jsonutils.JSONObject, error) {
- err := input.Validate()
- if err != nil {
- return nil, err
- }
- iBucket, err := bucket.GetIBucket(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "GetIBucket")
- }
- rules := []cloudprovider.SBucketCORSRule{}
- for i := range input.Data {
- rules = append(rules, cloudprovider.SBucketCORSRule{
- AllowedOrigins: input.Data[i].AllowedOrigins,
- AllowedMethods: input.Data[i].AllowedMethods,
- AllowedHeaders: input.Data[i].AllowedHeaders,
- MaxAgeSeconds: input.Data[i].MaxAgeSeconds,
- ExposeHeaders: input.Data[i].ExposeHeaders,
- Id: input.Data[i].Id,
- })
- }
- err = cloudprovider.SetBucketCORS(iBucket, rules)
- if err != nil {
- return nil, httperrors.NewInternalServerError("cloudprovider.SetBucketCORS error %s", err)
- }
- db.OpsLog.LogEvent(bucket, db.ACT_SET_CORS, rules, userCred)
- logclient.AddActionLogWithContext(ctx, bucket, logclient.ACT_SET_CORS, rules, userCred, true)
- return nil, nil
- }
- func (bucket *SBucket) PerformDeleteCors(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- input api.BucketCORSRuleDeleteInput,
- ) (jsonutils.JSONObject, error) {
- iBucket, err := bucket.GetIBucket(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "GetIBucket")
- }
- result, err := cloudprovider.DeleteBucketCORS(iBucket, input.Id)
- if err != nil {
- return nil, httperrors.NewInternalServerError("iBucket.DeleteCORS error %s", err)
- }
- db.OpsLog.LogEvent(bucket, db.ACT_DELETE_CORS, result, userCred)
- logclient.AddActionLogWithContext(ctx, bucket, logclient.ACT_DELETE_CORS, result, userCred, true)
- return nil, nil
- }
- func (bucket *SBucket) GetDetailsCors(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- input jsonutils.JSONObject,
- ) (api.BucketCORSRules, error) {
- rules := api.BucketCORSRules{}
- iBucket, err := bucket.GetIBucket(ctx)
- if err != nil {
- return rules, errors.Wrap(err, "GetIBucket")
- }
- corsRules, err := iBucket.GetCORSRules()
- if err != nil {
- return rules, httperrors.NewInternalServerError("iBucket.GetCORSRules error %s", err)
- }
- for i := range corsRules {
- rules.Data = append(rules.Data, api.BucketCORSRule{
- AllowedOrigins: corsRules[i].AllowedOrigins,
- AllowedMethods: corsRules[i].AllowedMethods,
- AllowedHeaders: corsRules[i].AllowedHeaders,
- MaxAgeSeconds: corsRules[i].MaxAgeSeconds,
- ExposeHeaders: corsRules[i].ExposeHeaders,
- Id: corsRules[i].Id,
- })
- }
- return rules, nil
- }
- func (bucket *SBucket) GetDetailsCdnDomain(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- input jsonutils.JSONObject,
- ) (api.CdnDomains, error) {
- domains := api.CdnDomains{}
- iBucket, err := bucket.GetIBucket(ctx)
- if err != nil {
- return domains, errors.Wrap(err, "GetIBucket")
- }
- cdnDomains, err := iBucket.GetCdnDomains()
- if err != nil {
- return domains, httperrors.NewInternalServerError("iBucket.GetCdnDomains error %s", err)
- }
- for i := range cdnDomains {
- domains.Data = append(domains.Data, api.CdnDomain{
- Domain: cdnDomains[i].Domain,
- Status: cdnDomains[i].Status,
- Area: cdnDomains[i].Area,
- Cname: cdnDomains[i].Cname,
- Origin: cdnDomains[i].Origin,
- OriginType: cdnDomains[i].OriginType,
- })
- }
- return domains, nil
- }
- func (bucket *SBucket) PerformSetReferer(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- input api.BucketRefererConf,
- ) (jsonutils.JSONObject, error) {
- err := input.Validate()
- if err != nil {
- return nil, err
- }
- iBucket, err := bucket.GetIBucket(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "GetIBucket")
- }
- conf := cloudprovider.SBucketRefererConf{
- Enabled: input.Enabled,
- DomainList: input.DomainList,
- RefererType: input.RefererType,
- AllowEmptyRefer: input.AllowEmptyRefer,
- }
- err = iBucket.SetReferer(conf)
- if err != nil {
- return nil, httperrors.NewInternalServerError("iBucket.SetRefer error %s", err)
- }
- db.OpsLog.LogEvent(bucket, db.ACT_SET_REFERER, conf, userCred)
- logclient.AddActionLogWithContext(ctx, bucket, logclient.ACT_SET_REFERER, conf, userCred, true)
- return nil, nil
- }
- func (bucket *SBucket) GetDetailsReferer(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- input jsonutils.JSONObject,
- ) (api.BucketRefererConf, error) {
- conf := api.BucketRefererConf{}
- iBucket, err := bucket.GetIBucket(ctx)
- if err != nil {
- return conf, errors.Wrap(err, "GetIBucket")
- }
- referConf, err := iBucket.GetReferer()
- if err != nil {
- return conf, httperrors.NewInternalServerError("iBucket.GetRefer error %s", err)
- }
- conf.Enabled = referConf.Enabled
- if conf.Enabled {
- conf.DomainList = referConf.DomainList
- conf.RefererType = referConf.RefererType
- conf.AllowEmptyRefer = referConf.AllowEmptyRefer
- }
- return conf, nil
- }
- func (bucket *SBucket) GetDetailsPolicy(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- input jsonutils.JSONObject,
- ) (api.BucketPolicy, error) {
- policy := api.BucketPolicy{}
- iBucket, err := bucket.GetIBucket(ctx)
- if err != nil {
- return policy, errors.Wrap(err, "GetIBucket")
- }
- policyStatements, err := iBucket.GetPolicy()
- if err != nil {
- return policy, httperrors.NewInternalServerError("iBucket.GetPolicy error %s", err)
- }
- for i := range policyStatements {
- policy.Data = append(policy.Data, api.BucketPolicyStatement{
- Principal: policyStatements[i].Principal,
- Action: policyStatements[i].Action,
- Effect: policyStatements[i].Effect,
- Resource: policyStatements[i].Resource,
- Condition: policyStatements[i].Condition,
- PrincipalId: policyStatements[i].PrincipalId,
- PrincipalNames: policyStatements[i].PrincipalNames,
- CannedAction: policyStatements[i].CannedAction,
- ResourcePath: policyStatements[i].ResourcePath,
- Id: policyStatements[i].Id,
- })
- }
- return policy, nil
- }
- func (bucket *SBucket) PerformSetPolicy(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- input api.BucketPolicyStatementInput,
- ) (jsonutils.JSONObject, error) {
- err := input.Validate()
- if err != nil {
- return nil, err
- }
- iBucket, err := bucket.GetIBucket(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "GetIBucket")
- }
- opts := cloudprovider.SBucketPolicyStatementInput{
- PrincipalId: input.PrincipalId,
- CannedAction: input.CannedAction,
- Effect: input.Effect,
- ResourcePath: input.ResourcePath,
- IpEquals: input.IpEquals,
- IpNotEquals: input.IpNotEquals,
- }
- err = iBucket.SetPolicy(opts)
- if err != nil {
- return nil, httperrors.NewInternalServerError("iBucket.SetPolicy error %s", err)
- }
- db.OpsLog.LogEvent(bucket, db.ACT_SET_POLICY, opts, userCred)
- logclient.AddActionLogWithContext(ctx, bucket, logclient.ACT_SET_POLICY, opts, userCred, true)
- return nil, nil
- }
- func (bucket *SBucket) PerformDeletePolicy(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- input api.BucketPolicyDeleteInput,
- ) (jsonutils.JSONObject, error) {
- iBucket, err := bucket.GetIBucket(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "GetIBucket")
- }
- result, err := iBucket.DeletePolicy(input.Id)
- if err != nil {
- return nil, httperrors.NewInternalServerError("iBucket.DeletePolicy error %s", err)
- }
- db.OpsLog.LogEvent(bucket, db.ACT_DELETE_POLICY, result, userCred)
- logclient.AddActionLogWithContext(ctx, bucket, logclient.ACT_DELETE_POLICY, result, userCred, true)
- return nil, nil
- }
- func (manager *SBucketManager) usageQByCloudEnv(q *sqlchemy.SQuery, providers []string, brands []string, cloudEnv string) *sqlchemy.SQuery {
- return CloudProviderFilter(q, q.Field("manager_id"), providers, brands, cloudEnv)
- }
- func (manager *SBucketManager) usageQByRanges(q *sqlchemy.SQuery, rangeObjs []db.IStandaloneModel) *sqlchemy.SQuery {
- return RangeObjectsFilter(q, rangeObjs, q.Field("cloudregion_id"), nil, q.Field("manager_id"), nil, nil)
- }
- func (manager *SBucketManager) usageQ(q *sqlchemy.SQuery, rangeObjs []db.IStandaloneModel, providers []string, brands []string, cloudEnv string) *sqlchemy.SQuery {
- q = manager.usageQByRanges(q, rangeObjs)
- q = manager.usageQByCloudEnv(q, providers, brands, cloudEnv)
- return q
- }
- type SBucketUsages struct {
- Buckets int
- Objects int
- Bytes int64
- BytesLimit int64
- DiskUsedRate float64
- }
- func (manager *SBucketManager) TotalCount(ctx context.Context, scope rbacscope.TRbacScope, ownerId mcclient.IIdentityProvider, rangeObjs []db.IStandaloneModel, providers []string, brands []string, cloudEnv string, policyResult rbacutils.SPolicyResult) SBucketUsages {
- usage := SBucketUsages{}
- bq := manager.Query()
- bq = db.ObjectIdQueryWithPolicyResult(ctx, bq, manager, policyResult)
- bq = scopeOwnerIdFilter(bq, scope, ownerId)
- buckets := bq.SubQuery()
- bucketsQ := buckets.Query(
- sqlchemy.NewFunction(
- sqlchemy.NewCase().When(
- sqlchemy.GE(buckets.Field("object_cnt"), 0),
- buckets.Field("object_cnt"),
- ).Else(sqlchemy.NewConstField(0)),
- "object_cnt1",
- false,
- ),
- sqlchemy.NewFunction(
- sqlchemy.NewCase().When(
- sqlchemy.GE(buckets.Field("size_bytes"), 0),
- buckets.Field("size_bytes"),
- ).Else(sqlchemy.NewConstField(0)),
- "size_bytes1",
- false,
- ),
- sqlchemy.NewFunction(
- sqlchemy.NewCase().When(
- sqlchemy.GT(buckets.Field("size_bytes_limit"), 0),
- buckets.Field("size_bytes_limit"),
- ).Else(
- buckets.Field("size_bytes")),
- "size_bytes_limit",
- false,
- ),
- )
- bucketsQ = manager.usageQ(bucketsQ, rangeObjs, providers, brands, cloudEnv)
- buckets = bucketsQ.SubQuery()
- q := buckets.Query(
- sqlchemy.COUNT("buckets"),
- sqlchemy.SUM("objects", buckets.Field("object_cnt1")),
- sqlchemy.SUM("bytes", buckets.Field("size_bytes1")),
- sqlchemy.SUM("bytes_limit", buckets.Field("size_bytes_limit")),
- )
- err := q.First(&usage)
- if err != nil {
- log.Errorf("Query bucket usage error %s", err)
- }
- if usage.BytesLimit > 0 {
- usage.DiskUsedRate = float64(usage.Bytes) / float64(usage.BytesLimit)
- }
- return usage
- }
- func (bucket *SBucket) PerformLimit(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- data jsonutils.JSONObject,
- ) (jsonutils.JSONObject, error) {
- if len(bucket.ExternalId) == 0 {
- return nil, httperrors.NewInvalidStatusError("no external bucket")
- }
- limit := cloudprovider.SBucketStats{}
- err := data.Unmarshal(&limit, "limit")
- if err != nil {
- return nil, httperrors.NewInputParameterError("unmarshal limit error %s", err)
- }
- iBucket, err := bucket.GetIBucket(ctx)
- if err != nil {
- return nil, errors.Wrap(err, "GetIBucket")
- }
- err = iBucket.SetLimit(limit)
- if err != nil && err != cloudprovider.ErrNotSupported {
- return nil, httperrors.NewInternalServerError("SetLimit error %s", err)
- }
- diff, err := db.Update(bucket, func() error {
- bucket.SizeBytesLimit = limit.SizeBytes
- bucket.ObjectCntLimit = limit.ObjectCount
- return nil
- })
- if err != nil {
- return nil, httperrors.NewInternalServerError("Update error %s", err)
- }
- if len(diff) > 0 {
- db.OpsLog.LogEvent(bucket, db.ACT_UPDATE, diff, userCred)
- logclient.AddActionLogWithContext(ctx, bucket, logclient.ACT_UPDATE, diff, userCred, true)
- }
- return nil, nil
- }
- func (bucket *SBucket) GetDetailsAccessInfo(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- ) (jsonutils.JSONObject, error) {
- if len(bucket.ExternalId) == 0 {
- return nil, httperrors.NewInvalidStatusError("no external bucket")
- }
- manager := bucket.GetCloudprovider()
- if manager == nil {
- return nil, httperrors.NewInternalServerError("missing manager?")
- }
- info, err := manager.GetDetailsClirc(ctx, userCred, nil)
- if err != nil {
- return nil, err
- }
- account, err := manager.GetCloudaccount()
- if err != nil {
- return nil, err
- }
- info.(*jsonutils.JSONDict).Add(jsonutils.NewString(account.Brand), "PROVIDER")
- return info, err
- }
- func (bucket *SBucket) GetUsages() []db.IUsage {
- if bucket.PendingDeleted || bucket.Deleted {
- return nil
- }
- usage := SRegionQuota{Bucket: 1}
- keys, err := bucket.GetQuotaKeys()
- if err != nil {
- log.Errorf("bucket.GetQuotaKeys fail %s", err)
- return nil
- }
- usage.SetKeys(keys)
- return []db.IUsage{
- &usage,
- }
- }
- func (bucket *SBucket) PerformMetadata(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- input api.BucketMetadataInput,
- ) (jsonutils.JSONObject, error) {
- err := input.Validate()
- if err != nil {
- return nil, err
- }
- _, objects, err := bucket.processObjectsActionInput(ctx, input.BucketObjectsActionInput)
- if err != nil {
- return nil, err
- }
- errs := make([]error, 0)
- for _, obj := range objects {
- err := obj.SetMeta(ctx, input.Metadata)
- if err != nil {
- errs = append(errs, errors.Wrap(err, obj.GetKey()))
- }
- }
- if len(errs) > 0 {
- return nil, errors.NewAggregate(errs)
- } else {
- return nil, nil
- }
- }
- func (bucket *SBucket) processObjectsActionInput(ctx context.Context, input api.BucketObjectsActionInput) (cloudprovider.ICloudBucket, []cloudprovider.ICloudObject, error) {
- if len(bucket.ExternalId) == 0 {
- return nil, nil, httperrors.NewInvalidStatusError("no external bucket")
- }
- iBucket, err := bucket.GetIBucket(ctx)
- if err != nil {
- return nil, nil, errors.Wrap(err, "GetIBucket")
- }
- objects := make([]cloudprovider.ICloudObject, 0)
- for _, key := range input.Key {
- if strings.HasSuffix(key, "/") {
- objs, err := cloudprovider.GetAllObjects(iBucket, key, true)
- if err != nil {
- return nil, nil, httperrors.NewInternalServerError("iBucket.GetIObjects error %s", err)
- }
- objects = append(objects, objs...)
- } else {
- object, err := cloudprovider.GetIObject(iBucket, key)
- if err != nil {
- if errors.Cause(err) == cloudprovider.ErrNotFound {
- return nil, nil, httperrors.NewResourceNotFoundError("object %s not found", key)
- } else {
- return nil, nil, httperrors.NewInternalServerError("iBucket.GetIObject error %s", err)
- }
- }
- objects = append(objects, object)
- }
- }
- return iBucket, objects, nil
- }
- func (bucket *SBucket) OnMetadataUpdated(ctx context.Context, userCred mcclient.TokenCredential) {
- if len(bucket.ExternalId) == 0 || options.Options.KeepTagLocalization {
- return
- }
- if account := bucket.GetCloudaccount(); account != nil && account.ReadOnly {
- return
- }
- iBucket, err := bucket.GetIBucket(ctx)
- if err != nil {
- return
- }
- tags, err := bucket.GetAllUserMetadata()
- if err != nil {
- return
- }
- diff, err := cloudprovider.SetBucketTags(ctx, iBucket, bucket.ManagerId, tags)
- if err != nil {
- logclient.AddSimpleActionLog(bucket, logclient.ACT_UPDATE_TAGS, err, userCred, false)
- return
- }
- if diff.IsChanged() {
- logclient.AddSimpleActionLog(bucket, logclient.ACT_UPDATE_TAGS, diff, userCred, true)
- }
- readOnly := false
- if account := bucket.GetCloudaccount(); account != nil {
- readOnly = account.ReadOnly
- }
- syncVirtualResourceMetadata(ctx, userCred, bucket, iBucket, readOnly)
- }
- func (manager *SBucketManager) ListItemExportKeys(ctx context.Context,
- q *sqlchemy.SQuery,
- userCred mcclient.TokenCredential,
- keys stringutils2.SSortedStrings,
- ) (*sqlchemy.SQuery, error) {
- q, err := manager.SSharableVirtualResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
- if err != nil {
- return nil, errors.Wrap(err, "SSharableVirtualResourceBaseManager.ListItemExportKeys")
- }
- if keys.ContainsAny(manager.SCloudregionResourceBaseManager.GetExportKeys()...) {
- q, err = manager.SCloudregionResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
- if err != nil {
- return nil, errors.Wrap(err, "SCloudregionResourceBaseManager.ListItemExportKeys")
- }
- }
- if keys.ContainsAny(manager.SManagedResourceBaseManager.GetExportKeys()...) {
- q, err = manager.SManagedResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
- if err != nil {
- return nil, errors.Wrap(err, "SManagedResourceBaseManager.ListItemExportKeys")
- }
- }
- return q, nil
- }
|