github.com/muhammadn/cortex@v1.9.1-0.20220510110439-46bb7000d03d/pkg/chunk/purger/tenant_deletion_api.go (about) 1 package purger 2 3 import ( 4 "context" 5 "net/http" 6 "strings" 7 "time" 8 9 "github.com/go-kit/log" 10 "github.com/go-kit/log/level" 11 "github.com/oklog/ulid" 12 "github.com/pkg/errors" 13 "github.com/prometheus/client_golang/prometheus" 14 "github.com/thanos-io/thanos/pkg/objstore" 15 16 "github.com/cortexproject/cortex/pkg/storage/bucket" 17 cortex_tsdb "github.com/cortexproject/cortex/pkg/storage/tsdb" 18 "github.com/cortexproject/cortex/pkg/tenant" 19 "github.com/cortexproject/cortex/pkg/util" 20 ) 21 22 type TenantDeletionAPI struct { 23 bucketClient objstore.Bucket 24 logger log.Logger 25 cfgProvider bucket.TenantConfigProvider 26 } 27 28 func NewTenantDeletionAPI(storageCfg cortex_tsdb.BlocksStorageConfig, cfgProvider bucket.TenantConfigProvider, logger log.Logger, reg prometheus.Registerer) (*TenantDeletionAPI, error) { 29 bucketClient, err := createBucketClient(storageCfg, logger, reg) 30 if err != nil { 31 return nil, err 32 } 33 34 return newTenantDeletionAPI(bucketClient, cfgProvider, logger), nil 35 } 36 37 func newTenantDeletionAPI(bkt objstore.Bucket, cfgProvider bucket.TenantConfigProvider, logger log.Logger) *TenantDeletionAPI { 38 return &TenantDeletionAPI{ 39 bucketClient: bkt, 40 cfgProvider: cfgProvider, 41 logger: logger, 42 } 43 } 44 45 func (api *TenantDeletionAPI) DeleteTenant(w http.ResponseWriter, r *http.Request) { 46 ctx := r.Context() 47 userID, err := tenant.TenantID(ctx) 48 if err != nil { 49 // When Cortex is running, it uses Auth Middleware for checking X-Scope-OrgID and injecting tenant into context. 50 // Auth Middleware sends http.StatusUnauthorized if X-Scope-OrgID is missing, so we do too here, for consistency. 51 http.Error(w, err.Error(), http.StatusUnauthorized) 52 return 53 } 54 55 err = cortex_tsdb.WriteTenantDeletionMark(r.Context(), api.bucketClient, userID, api.cfgProvider, cortex_tsdb.NewTenantDeletionMark(time.Now())) 56 if err != nil { 57 level.Error(api.logger).Log("msg", "failed to write tenant deletion mark", "user", userID, "err", err) 58 59 http.Error(w, err.Error(), http.StatusInternalServerError) 60 return 61 } 62 63 level.Info(api.logger).Log("msg", "tenant deletion mark in blocks storage created", "user", userID) 64 65 w.WriteHeader(http.StatusOK) 66 } 67 68 type DeleteTenantStatusResponse struct { 69 TenantID string `json:"tenant_id"` 70 BlocksDeleted bool `json:"blocks_deleted"` 71 } 72 73 func (api *TenantDeletionAPI) DeleteTenantStatus(w http.ResponseWriter, r *http.Request) { 74 ctx := r.Context() 75 userID, err := tenant.TenantID(ctx) 76 if err != nil { 77 http.Error(w, err.Error(), http.StatusBadRequest) 78 return 79 } 80 81 result := DeleteTenantStatusResponse{} 82 result.TenantID = userID 83 result.BlocksDeleted, err = api.isBlocksForUserDeleted(ctx, userID) 84 if err != nil { 85 http.Error(w, err.Error(), http.StatusInternalServerError) 86 return 87 } 88 89 util.WriteJSONResponse(w, result) 90 } 91 92 func (api *TenantDeletionAPI) isBlocksForUserDeleted(ctx context.Context, userID string) (bool, error) { 93 var errBlockFound = errors.New("block found") 94 95 userBucket := bucket.NewUserBucketClient(userID, api.bucketClient, api.cfgProvider) 96 err := userBucket.Iter(ctx, "", func(s string) error { 97 s = strings.TrimSuffix(s, "/") 98 99 _, err := ulid.Parse(s) 100 if err != nil { 101 // not block, keep looking 102 return nil 103 } 104 105 // Used as shortcut to stop iteration. 106 return errBlockFound 107 }) 108 109 if errors.Is(err, errBlockFound) { 110 return false, nil 111 } 112 113 if err != nil { 114 return false, err 115 } 116 117 // No blocks found, all good. 118 return true, nil 119 } 120 121 func createBucketClient(cfg cortex_tsdb.BlocksStorageConfig, logger log.Logger, reg prometheus.Registerer) (objstore.Bucket, error) { 122 bucketClient, err := bucket.NewClient(context.Background(), cfg.Bucket, "purger", logger, reg) 123 if err != nil { 124 return nil, errors.Wrap(err, "create bucket client") 125 } 126 127 return bucketClient, nil 128 }