feat(delivery): delete CloudDM datasource when instance is removed
This commit is contained in:
@@ -67,6 +67,7 @@ DELIVERY_MYSQL_INSPECT_TIMEOUT_SECONDS=90
|
||||
DELIVERY_ROLLBACK_TEMPLATE_ID=0
|
||||
DELIVERY_SERVICE_TOKEN=
|
||||
CLOUDDM_REGISTER_URL=
|
||||
CLOUDDM_DELETE_URL=
|
||||
CLOUDDM_API_TOKEN=
|
||||
|
||||
JWT_SECRET=change-this-secret
|
||||
|
||||
@@ -62,6 +62,7 @@ type Config struct {
|
||||
CloudDMAdminUsername string
|
||||
CloudDMAdminPassword string
|
||||
CloudDMRegisterURL string
|
||||
CloudDMDeleteURL string
|
||||
CloudDMAPIToken string
|
||||
AWXBaseURL string
|
||||
AWXToken string
|
||||
@@ -147,6 +148,7 @@ func Load() Config {
|
||||
CloudDMAdminUsername: env("CLOUDDM_ADMIN_USERNAME", ""),
|
||||
CloudDMAdminPassword: env("CLOUDDM_ADMIN_PASSWORD", ""),
|
||||
CloudDMRegisterURL: trimURL(env("CLOUDDM_REGISTER_URL", "")),
|
||||
CloudDMDeleteURL: trimURL(env("CLOUDDM_DELETE_URL", "")),
|
||||
CloudDMAPIToken: env("CLOUDDM_API_TOKEN", ""),
|
||||
AWXBaseURL: trimURL(env("AWX_BASE_URL", "")),
|
||||
AWXToken: env("AWX_TOKEN", ""),
|
||||
|
||||
@@ -1215,6 +1215,25 @@ func (s *DeliveryService) applyMySQLInspectResults(ctx context.Context, results
|
||||
Updates(map[string]any{"status": "released", "released_at": now, "updated_at": now}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dataSourceID, deleteStatus := cloudDMDeleteInfo(meta)
|
||||
if dataSourceID > 0 && deleteStatus != "deleted" {
|
||||
if err := s.deleteCloudDMDataSource(ctx, current); err != nil {
|
||||
meta = updateCloudDMDeleteMetadata(meta, "failed", err.Error(), "")
|
||||
if updateErr := s.db.WithContext(ctx).Model(&model.DeploymentResult{}).
|
||||
Where("id = ?", result.ID).
|
||||
Update("metadata", string(mustJSON(meta))).Error; updateErr != nil {
|
||||
return updateErr
|
||||
}
|
||||
return err
|
||||
}
|
||||
meta = updateCloudDMDeleteMetadata(meta, "deleted", "", now.Format(time.RFC3339))
|
||||
if err := s.db.WithContext(ctx).Model(&model.DeploymentResult{}).
|
||||
Where("id = ?", result.ID).
|
||||
Update("metadata", string(mustJSON(meta))).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -2384,6 +2403,12 @@ type cloudDMRegisterRequest struct {
|
||||
DataSource cloudDMDataSource `json:"dataSource"`
|
||||
}
|
||||
|
||||
type cloudDMDeleteRequest struct {
|
||||
SourceSystem string `json:"sourceSystem"`
|
||||
ResourceType string `json:"resourceType"`
|
||||
ExternalResourceID string `json:"externalResourceId"`
|
||||
}
|
||||
|
||||
type cloudDMDataSource struct {
|
||||
InstanceName string `json:"instanceName"`
|
||||
InstanceDesc string `json:"instanceDesc"`
|
||||
@@ -2463,6 +2488,36 @@ func cloudDMMetadata(existing map[string]any, instance model.DeploymentResult, d
|
||||
return meta
|
||||
}
|
||||
|
||||
func buildCloudDMDeleteRequest(instance model.DeploymentResult) cloudDMDeleteRequest {
|
||||
return cloudDMDeleteRequest{
|
||||
SourceSystem: "xinfra",
|
||||
ResourceType: "MYSQL_INSTANCE",
|
||||
ExternalResourceID: cloudDMExternalResourceID(instance.ID),
|
||||
}
|
||||
}
|
||||
|
||||
func cloudDMDeleteInfo(meta map[string]any) (uint64, string) {
|
||||
clouddm, ok := meta["clouddm"].(map[string]any)
|
||||
if !ok {
|
||||
return 0, ""
|
||||
}
|
||||
dataSourceID, _ := cloudDMDataSourceIDFromValue(clouddm["data_source_id"])
|
||||
deleteStatus := strings.ToLower(strings.TrimSpace(stringValue(clouddm["delete_status"])))
|
||||
return dataSourceID, deleteStatus
|
||||
}
|
||||
|
||||
func updateCloudDMDeleteMetadata(meta map[string]any, deleteStatus, deleteError, deletedAt string) map[string]any {
|
||||
clouddm, ok := meta["clouddm"].(map[string]any)
|
||||
if !ok {
|
||||
clouddm = map[string]any{}
|
||||
}
|
||||
clouddm["delete_status"] = deleteStatus
|
||||
clouddm["delete_error"] = deleteError
|
||||
clouddm["deleted_at"] = deletedAt
|
||||
meta["clouddm"] = clouddm
|
||||
return meta
|
||||
}
|
||||
|
||||
func buildCloudDMRegisterRequest(instance model.DeploymentResult, payload deliveryPayload, password string) cloudDMRegisterRequest {
|
||||
description := strings.TrimSpace(payload.InstanceDesc)
|
||||
if description == "" {
|
||||
@@ -2498,6 +2553,37 @@ func buildCloudDMRegisterRequest(instance model.DeploymentResult, payload delive
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DeliveryService) deleteCloudDMDataSource(ctx context.Context, instance model.DeploymentResult) error {
|
||||
if strings.TrimSpace(s.cfg.CloudDMDeleteURL) == "" {
|
||||
return fmt.Errorf("CloudDM deletion requires CLOUDDM_DELETE_URL to be configured")
|
||||
}
|
||||
if strings.TrimSpace(s.cfg.CloudDMAPIToken) == "" {
|
||||
return fmt.Errorf("CloudDM deletion requires CLOUDDM_API_TOKEN to be configured")
|
||||
}
|
||||
body := buildCloudDMDeleteRequest(instance)
|
||||
raw, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("encode CloudDM deletion request: %w", err)
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, s.cfg.CloudDMDeleteURL, bytes.NewReader(raw))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Authorization", "Bearer "+strings.TrimSpace(s.cfg.CloudDMAPIToken))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("X-Request-ID", randomUUID())
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
respBody, _ := io.ReadAll(resp.Body)
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
return fmt.Errorf("CloudDM deletion returned %s: %s", resp.Status, strings.TrimSpace(string(respBody)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *DeliveryService) RegisterCloudDM(ctx context.Context, taskID string) error {
|
||||
if strings.TrimSpace(s.cfg.CloudDMRegisterURL) == "" {
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user