You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

resty.go 14 kB

3 years ago
3 years ago
3 years ago
4 years ago
2 years ago
3 years ago
3 years ago
2 years ago
4 years ago
4 years ago
2 years ago
2 years ago
2 years ago
2 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
2 years ago
3 years ago
2 years ago
3 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. package cloudbrain
  2. import (
  3. "code.gitea.io/gitea/modules/notification"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "net/http"
  8. "strconv"
  9. "strings"
  10. "time"
  11. "code.gitea.io/gitea/modules/log"
  12. "code.gitea.io/gitea/models"
  13. "code.gitea.io/gitea/modules/setting"
  14. "github.com/go-resty/resty/v2"
  15. )
  16. var (
  17. restyClient *resty.Client
  18. HOST string
  19. TOKEN string
  20. ImagesUrlMap = map[string]string{Public: "/rest-server/api/v1/image/public/list/", Custom: "/rest-server/api/v1/image/list/"}
  21. )
  22. const (
  23. JobHasBeenStopped = "S410"
  24. Public = "public"
  25. Custom = "custom"
  26. LogPageSize = 500
  27. errInvalidToken = "S401"
  28. LogPageTokenExpired = "5m"
  29. pageSize = 15
  30. QueuesDetailUrl = "/rest-server/api/v2/queuesdetail"
  31. )
  32. func getRestyClient() *resty.Client {
  33. if restyClient == nil {
  34. restyClient = resty.New()
  35. }
  36. return restyClient
  37. }
  38. func checkSetting() {
  39. if len(HOST) != 0 && len(TOKEN) != 0 && restyClient != nil {
  40. return
  41. }
  42. _ = loginCloudbrain()
  43. }
  44. func loginCloudbrain() error {
  45. conf := setting.GetCloudbrainConfig()
  46. username := conf.Username
  47. password := conf.Password
  48. HOST = conf.Host
  49. var loginResult models.CloudBrainLoginResult
  50. client := getRestyClient()
  51. res, err := client.R().
  52. SetHeader("Content-Type", "application/json").
  53. SetBody(map[string]interface{}{"username": username, "password": password, "expiration": conf.Expiration}).
  54. SetResult(&loginResult).
  55. Post(HOST + "/rest-server/api/v1/token")
  56. if err != nil {
  57. return fmt.Errorf("resty loginCloudbrain: %s", err)
  58. }
  59. if loginResult.Code != Success {
  60. return fmt.Errorf("%s: %s", loginResult.Msg, res.String())
  61. }
  62. TOKEN = loginResult.Payload["token"].(string)
  63. return nil
  64. }
  65. func GetQueuesDetail() (*map[string]int, error) {
  66. checkSetting()
  67. client := getRestyClient()
  68. var jobResult models.QueueDetailResult
  69. var result = make(map[string]int, 0)
  70. retry := 0
  71. sendjob:
  72. res, err := client.R().
  73. SetHeader("Content-Type", "application/json").
  74. SetAuthToken(TOKEN).
  75. SetResult(&jobResult).
  76. Get(HOST + QueuesDetailUrl)
  77. if err != nil {
  78. return nil, fmt.Errorf("resty get queues detail failed: %s", err)
  79. }
  80. if (res.StatusCode() == http.StatusUnauthorized || jobResult.Code == errInvalidToken) && retry < 1 {
  81. retry++
  82. _ = loginCloudbrain()
  83. goto sendjob
  84. }
  85. if jobResult.Code != Success {
  86. return nil, fmt.Errorf("jobResult err: %s", res.String())
  87. }
  88. for k, v := range jobResult.Payload {
  89. result[k] = v.JobScheduleInfo.Pending
  90. }
  91. return &result, nil
  92. }
  93. func CreateJob(jobName string, createJobParams models.CreateJobParams) (*models.CreateJobResult, error) {
  94. checkSetting()
  95. client := getRestyClient()
  96. var jobResult models.CreateJobResult
  97. retry := 0
  98. reqPara, _ := json.Marshal(createJobParams)
  99. log.Warn("job req:", string(reqPara[:]))
  100. sendjob:
  101. res, err := client.R().
  102. SetHeader("Content-Type", "application/json").
  103. SetAuthToken(TOKEN).
  104. SetBody(createJobParams).
  105. SetResult(&jobResult).
  106. Post(HOST + "/rest-server/api/v1/jobs/")
  107. if err != nil {
  108. return nil, fmt.Errorf("resty create job: %s", err)
  109. }
  110. var response models.CloudBrainResult
  111. json.Unmarshal(res.Body(), &response)
  112. if response.Code == errInvalidToken && retry < 1 {
  113. retry++
  114. _ = loginCloudbrain()
  115. goto sendjob
  116. }
  117. if jobResult.Code != Success {
  118. return &jobResult, fmt.Errorf("jobResult err: %s", res.String())
  119. }
  120. return &jobResult, nil
  121. }
  122. func GetJob(jobID string) (*models.GetJobResult, error) {
  123. checkSetting()
  124. // http://192.168.204.24/rest-server/api/v1/jobs/90e26e500c4b3011ea0a251099a987938b96
  125. client := getRestyClient()
  126. var getJobResult models.GetJobResult
  127. retry := 0
  128. sendjob:
  129. res, err := client.R().
  130. SetHeader("Content-Type", "application/json").
  131. SetAuthToken(TOKEN).
  132. SetResult(&getJobResult).
  133. Get(HOST + "/rest-server/api/v1/jobs/" + jobID)
  134. if err != nil {
  135. return nil, fmt.Errorf("resty GetJob: %v", err)
  136. }
  137. var response models.CloudBrainResult
  138. json.Unmarshal(res.Body(), &response)
  139. if response.Code == errInvalidToken && retry < 1 {
  140. retry++
  141. _ = loginCloudbrain()
  142. goto sendjob
  143. }
  144. if getJobResult.Code != Success {
  145. return &getJobResult, fmt.Errorf("jobResult GetJob err: %s", res.String())
  146. }
  147. return &getJobResult, nil
  148. }
  149. func GetImagesPageable(page int, size int, imageType string, name string) (*models.GetImagesResult, error) {
  150. checkSetting()
  151. client := getRestyClient()
  152. var getImagesResult models.GetImagesResult
  153. retry := 0
  154. sendjob:
  155. res, err := client.R().
  156. SetHeader("Content-Type", "application/json").
  157. SetAuthToken(TOKEN).
  158. SetQueryString(getQueryString(page, size, name)).
  159. SetResult(&getImagesResult).
  160. Get(HOST + ImagesUrlMap[imageType])
  161. if err != nil {
  162. return nil, fmt.Errorf("resty GetImages: %v", err)
  163. }
  164. var response models.CloudBrainResult
  165. json.Unmarshal(res.Body(), &response)
  166. if response.Code == errInvalidToken && retry < 1 {
  167. retry++
  168. _ = loginCloudbrain()
  169. goto sendjob
  170. }
  171. if getImagesResult.Code != Success {
  172. return &getImagesResult, fmt.Errorf("getImagesResult err: %s", res.String())
  173. }
  174. getImagesResult.Payload.TotalPages = getTotalPages(getImagesResult, size)
  175. return &getImagesResult, nil
  176. }
  177. func getTotalPages(getImagesResult models.GetImagesResult, size int) int {
  178. totalCount := getImagesResult.Payload.Count
  179. var totalPages int
  180. if totalCount%size != 0 {
  181. totalPages = totalCount/size + 1
  182. } else {
  183. totalPages = totalCount / size
  184. }
  185. return totalPages
  186. }
  187. func getQueryString(page int, size int, name string) string {
  188. if strings.TrimSpace(name) == "" {
  189. return fmt.Sprintf("pageIndex=%d&pageSize=%d", page, size)
  190. }
  191. return fmt.Sprintf("pageIndex=%d&pageSize=%d&name=%s", page, size, name)
  192. }
  193. func CommitImage(jobID string, params models.CommitImageParams, doer *models.User) error {
  194. imageTag := strings.TrimSpace(params.ImageTag)
  195. dbImage, err := models.GetImageByTag(imageTag)
  196. if err != nil && !models.IsErrImageNotExist(err) {
  197. return fmt.Errorf("resty CommitImage: %v", err)
  198. }
  199. var createTime time.Time
  200. var isSetCreatedUnix = false
  201. if dbImage != nil {
  202. if dbImage.UID != params.UID {
  203. return models.ErrorImageTagExist{
  204. Tag: imageTag,
  205. }
  206. } else {
  207. if dbImage.Status == models.IMAGE_STATUS_COMMIT {
  208. return models.ErrorImageCommitting{
  209. Tag: imageTag,
  210. }
  211. } else { //覆盖提交
  212. result, err := GetImagesPageable(1, pageSize, Custom, "")
  213. if err == nil && result.Code == "S000" {
  214. for _, v := range result.Payload.ImageInfo {
  215. if v.Place == dbImage.Place {
  216. isSetCreatedUnix = true
  217. createTime, _ = time.Parse(time.RFC3339, v.Createtime)
  218. break
  219. }
  220. }
  221. }
  222. }
  223. }
  224. }
  225. checkSetting()
  226. client := getRestyClient()
  227. var result models.CommitImageResult
  228. retry := 0
  229. sendjob:
  230. res, err := client.R().
  231. SetHeader("Content-Type", "application/json").
  232. SetAuthToken(TOKEN).
  233. SetBody(params.CommitImageCloudBrainParams).
  234. SetResult(&result).
  235. Post(HOST + "/rest-server/api/v1/jobs/" + jobID + "/commitImage")
  236. if err != nil {
  237. return fmt.Errorf("resty CommitImage: %v", err)
  238. }
  239. var response models.CloudBrainResult
  240. json.Unmarshal(res.Body(), &response)
  241. if response.Code == errInvalidToken && retry < 1 {
  242. retry++
  243. _ = loginCloudbrain()
  244. goto sendjob
  245. }
  246. if result.Code != Success {
  247. return fmt.Errorf("CommitImage err: %s", res.String())
  248. }
  249. image := models.Image{
  250. Type: models.NORMAL_TYPE,
  251. CloudbrainType: params.CloudBrainType,
  252. UID: params.UID,
  253. IsPrivate: params.IsPrivate,
  254. Tag: imageTag,
  255. Description: params.ImageDescription,
  256. Place: setting.Cloudbrain.ImageURLPrefix + imageTag,
  257. Status: models.IMAGE_STATUS_COMMIT,
  258. }
  259. err = models.WithTx(func(ctx models.DBContext) error {
  260. models.UpdateAutoIncrementIndex()
  261. if dbImage != nil {
  262. dbImage.IsPrivate = params.IsPrivate
  263. dbImage.Description = params.ImageDescription
  264. dbImage.Status = models.IMAGE_STATUS_COMMIT
  265. image = *dbImage
  266. if err := models.UpdateLocalImage(dbImage); err != nil {
  267. log.Error("Failed to update image record.", err)
  268. return fmt.Errorf("CommitImage err: %s", res.String())
  269. }
  270. } else {
  271. if err := models.CreateLocalImage(&image); err != nil {
  272. log.Error("Failed to insert image record.", err)
  273. return fmt.Errorf("CommitImage err: %s", res.String())
  274. }
  275. }
  276. if err := models.SaveImageTopics(image.ID, params.Topics...); err != nil {
  277. log.Error("Failed to insert image record.", err)
  278. return fmt.Errorf("CommitImage err: %s", res.String())
  279. }
  280. return nil
  281. })
  282. if err == nil {
  283. go updateImageStatus(image, isSetCreatedUnix, createTime)
  284. notification.NotifyCreateImage(doer, image)
  285. }
  286. return err
  287. }
  288. func CommitAdminImage(params models.CommitImageParams, doer *models.User) error {
  289. imageTag := strings.TrimSpace(params.ImageTag)
  290. exist, err := models.IsImageExist(imageTag)
  291. if err != nil {
  292. return fmt.Errorf("resty CommitImage: %v", err)
  293. }
  294. if exist {
  295. return models.ErrorImageTagExist{
  296. Tag: imageTag,
  297. }
  298. }
  299. image := models.Image{
  300. CloudbrainType: params.CloudBrainType,
  301. UID: params.UID,
  302. IsPrivate: params.IsPrivate,
  303. Tag: imageTag,
  304. Description: params.ImageDescription,
  305. Place: params.Place,
  306. Status: models.IMAGE_STATUS_SUCCESS,
  307. Type: params.Type,
  308. }
  309. err = models.WithTx(func(ctx models.DBContext) error {
  310. if err := models.CreateLocalImage(&image); err != nil {
  311. log.Error("Failed to insert image record.", err)
  312. return fmt.Errorf("resty CommitImage: %v", err)
  313. }
  314. if err := models.SaveImageTopics(image.ID, params.Topics...); err != nil {
  315. log.Error("Failed to insert image record.", err)
  316. return fmt.Errorf("resty CommitImage: %v", err)
  317. }
  318. return nil
  319. })
  320. if err == nil {
  321. notification.NotifyCreateImage(doer, image)
  322. }
  323. return err
  324. }
  325. func updateImageStatus(image models.Image, isSetCreatedUnix bool, createTime time.Time) {
  326. attemps := 60
  327. commitSuccess := false
  328. for i := 0; i < attemps; i++ {
  329. time.Sleep(20 * time.Second)
  330. log.Info("the " + strconv.Itoa(i) + " times query cloudbrain images.Imagetag:" + image.Tag + "isSetCreate:" + strconv.FormatBool(isSetCreatedUnix))
  331. result, err := GetImagesPageable(1, pageSize, Custom, "")
  332. if err == nil && result.Code == "S000" {
  333. log.Info("images count:" + strconv.Itoa(result.Payload.Count))
  334. for _, v := range result.Payload.ImageInfo {
  335. if v.Place == image.Place && (!isSetCreatedUnix || (isSetCreatedUnix && createTimeUpdated(v, createTime))) {
  336. image.Status = models.IMAGE_STATUS_SUCCESS
  337. models.UpdateLocalImageStatus(&image)
  338. commitSuccess = true
  339. break
  340. }
  341. }
  342. }
  343. if commitSuccess {
  344. break
  345. }
  346. }
  347. if !commitSuccess {
  348. image.Status = models.IMAGE_STATUS_Failed
  349. models.UpdateLocalImageStatus(&image)
  350. }
  351. }
  352. func createTimeUpdated(v *models.ImageInfo, createTime time.Time) bool {
  353. newTime, err := time.Parse(time.RFC3339, v.Createtime)
  354. if err != nil {
  355. return false
  356. }
  357. return newTime.After(createTime)
  358. }
  359. func StopJob(jobID string) error {
  360. checkSetting()
  361. client := getRestyClient()
  362. var result models.CloudBrainResult
  363. retry := 0
  364. sendjob:
  365. res, err := client.R().
  366. SetHeader("Content-Type", "application/json").
  367. SetAuthToken(TOKEN).
  368. SetResult(&result).
  369. Delete(HOST + "/rest-server/api/v1/jobs/" + jobID)
  370. if err != nil {
  371. return fmt.Errorf("resty StopJob: %v", err)
  372. }
  373. var response models.CloudBrainResult
  374. json.Unmarshal(res.Body(), &response)
  375. if response.Code == errInvalidToken && retry < 1 {
  376. retry++
  377. _ = loginCloudbrain()
  378. goto sendjob
  379. }
  380. if result.Code != Success {
  381. if result.Code == JobHasBeenStopped {
  382. log.Info("StopJob(%s) failed:%s", jobID, result.Msg)
  383. } else {
  384. return fmt.Errorf("StopJob err: %s", res.String())
  385. }
  386. }
  387. return nil
  388. }
  389. func GetJobLog(jobID string) (*models.GetJobLogResult, error) {
  390. checkSetting()
  391. client := getRestyClient()
  392. var result models.GetJobLogResult
  393. req := models.GetJobLogParams{
  394. Size: strconv.Itoa(LogPageSize),
  395. Sort: "log.offset",
  396. QueryInfo: models.QueryInfo{
  397. MatchInfo: models.MatchInfo{
  398. PodName: jobID + "-task1-0",
  399. },
  400. },
  401. }
  402. res, err := client.R().
  403. SetHeader("Content-Type", "application/json").
  404. SetAuthToken(TOKEN).
  405. SetBody(req).
  406. SetResult(&result).
  407. Post(HOST + "es/_search?_source=message&scroll=" + LogPageTokenExpired)
  408. if err != nil {
  409. log.Error("GetJobLog failed: %v", err)
  410. return &result, fmt.Errorf("resty GetJobLog: %v, %s", err, res.String())
  411. }
  412. if !strings.Contains(res.Status(), strconv.Itoa(http.StatusOK)) {
  413. log.Error("res.Status(): %s, response: %s", res.Status(), res.String())
  414. return &result, errors.New(res.String())
  415. }
  416. return &result, nil
  417. }
  418. func GetJobAllLog(scrollID string) (*models.GetJobLogResult, error) {
  419. checkSetting()
  420. client := getRestyClient()
  421. var result models.GetJobLogResult
  422. req := models.GetAllJobLogParams{
  423. Scroll: LogPageTokenExpired,
  424. ScrollID: scrollID,
  425. }
  426. res, err := client.R().
  427. SetHeader("Content-Type", "application/json").
  428. SetAuthToken(TOKEN).
  429. SetBody(req).
  430. SetResult(&result).
  431. Post(HOST + "es/_search/scroll")
  432. if err != nil {
  433. log.Error("GetJobAllLog failed: %v", err)
  434. return &result, fmt.Errorf("resty GetJobAllLog: %v, %s", err, res.String())
  435. }
  436. if !strings.Contains(res.Status(), strconv.Itoa(http.StatusOK)) {
  437. log.Error("res.Status(): %s, response: %s", res.Status(), res.String())
  438. return &result, errors.New(res.String())
  439. }
  440. return &result, nil
  441. }
  442. func DeleteJobLogToken(scrollID string) error {
  443. checkSetting()
  444. client := getRestyClient()
  445. var result models.DeleteJobLogTokenResult
  446. req := models.DeleteJobLogTokenParams{
  447. ScrollID: scrollID,
  448. }
  449. res, err := client.R().
  450. SetHeader("Content-Type", "application/json").
  451. SetAuthToken(TOKEN).
  452. SetBody(req).
  453. SetResult(&result).
  454. Delete(HOST + "es/_search/scroll")
  455. if err != nil {
  456. log.Error("DeleteJobLogToken failed: %v", err)
  457. return fmt.Errorf("resty DeleteJobLogToken: %v, %s", err, res.String())
  458. }
  459. if !strings.Contains(res.Status(), strconv.Itoa(http.StatusOK)) {
  460. log.Error("res.Status(): %s, response: %s", res.Status(), res.String())
  461. return errors.New(res.String())
  462. }
  463. if !result.Succeeded {
  464. log.Error("DeleteJobLogToken failed")
  465. return errors.New("DeleteJobLogToken failed")
  466. }
  467. return nil
  468. }