containers.go 53 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package models
  15. import (
  16. "context"
  17. "encoding/base64"
  18. "fmt"
  19. "path/filepath"
  20. "strings"
  21. "time"
  22. "yunion.io/x/jsonutils"
  23. "yunion.io/x/log"
  24. "yunion.io/x/pkg/errors"
  25. "yunion.io/x/pkg/gotypes"
  26. "yunion.io/x/pkg/util/sets"
  27. "yunion.io/x/sqlchemy"
  28. "yunion.io/x/onecloud/pkg/apis"
  29. api "yunion.io/x/onecloud/pkg/apis/compute"
  30. hostapi "yunion.io/x/onecloud/pkg/apis/host"
  31. identityapi "yunion.io/x/onecloud/pkg/apis/identity"
  32. imageapi "yunion.io/x/onecloud/pkg/apis/image"
  33. "yunion.io/x/onecloud/pkg/cloudcommon/consts"
  34. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  35. "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
  36. "yunion.io/x/onecloud/pkg/compute/options"
  37. "yunion.io/x/onecloud/pkg/compute/utils"
  38. "yunion.io/x/onecloud/pkg/httperrors"
  39. "yunion.io/x/onecloud/pkg/mcclient"
  40. "yunion.io/x/onecloud/pkg/mcclient/auth"
  41. identitymod "yunion.io/x/onecloud/pkg/mcclient/modules/identity"
  42. kubemod "yunion.io/x/onecloud/pkg/mcclient/modules/k8s"
  43. )
  44. var containerManager *SContainerManager
  45. func GetContainerManager() *SContainerManager {
  46. if containerManager == nil {
  47. containerManager = &SContainerManager{
  48. SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager(
  49. SContainer{},
  50. "containers_tbl",
  51. "container",
  52. "containers"),
  53. }
  54. containerManager.SetVirtualObject(containerManager)
  55. }
  56. return containerManager
  57. }
  58. func init() {
  59. GetContainerManager()
  60. }
  61. type SContainerManager struct {
  62. db.SVirtualResourceBaseManager
  63. }
  64. type SContainer struct {
  65. db.SVirtualResourceBase
  66. db.SExternalizedResourceBase
  67. // GuestId is also the pod id
  68. GuestId string `width:"36" charset:"ascii" create:"required" list:"user" index:"true"`
  69. // Spec stores all container running options
  70. Spec *api.ContainerSpec `length:"long" create:"required" list:"user" update:"user"`
  71. // 启动时间
  72. StartedAt time.Time `nullable:"true" created_at:"false" index:"true" get:"user" list:"user" json:"started_at"`
  73. // 上次退出时间
  74. LastFinishedAt time.Time `nullable:"true" created_at:"false" index:"true" get:"user" list:"user" json:"last_finished_at"`
  75. // 重启次数
  76. RestartCount int `nullable:"true" list:"user"`
  77. }
  78. func (m *SContainerManager) CreateOnPod(
  79. ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider,
  80. pod *SGuest, data *api.PodContainerCreateInput) (*SContainer, error) {
  81. input := &api.ContainerCreateInput{
  82. GuestId: pod.GetId(),
  83. Spec: data.ContainerSpec,
  84. SkipTask: true,
  85. }
  86. input.Name = data.Name
  87. obj, err := db.DoCreate(m, ctx, userCred, nil, jsonutils.Marshal(input), ownerId)
  88. if err != nil {
  89. return nil, errors.Wrap(err, "create container")
  90. }
  91. return obj.(*SContainer), nil
  92. }
  93. func (m *SContainerManager) FetchUniqValues(ctx context.Context, data jsonutils.JSONObject) jsonutils.JSONObject {
  94. guestId, _ := data.GetString("guest_id")
  95. return jsonutils.Marshal(map[string]string{"guest_id": guestId})
  96. }
  97. func (m *SContainerManager) FilterByUniqValues(q *sqlchemy.SQuery, values jsonutils.JSONObject) *sqlchemy.SQuery {
  98. guestId, _ := values.GetString("guest_id")
  99. if len(guestId) > 0 {
  100. q = q.Equals("guest_id", guestId)
  101. }
  102. return q
  103. }
  104. func (m *SContainerManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query api.ContainerListInput) (*sqlchemy.SQuery, error) {
  105. q, err := m.SVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, query.VirtualResourceListInput)
  106. if err != nil {
  107. return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ListItemFilter")
  108. }
  109. if query.GuestId != "" {
  110. gst, err := GuestManager.FetchByIdOrName(ctx, userCred, query.GuestId)
  111. if err != nil {
  112. return nil, errors.Wrapf(err, "fetch guest by %s", query.GuestId)
  113. }
  114. q = q.Equals("guest_id", gst.GetId())
  115. }
  116. if query.HostId != "" {
  117. host, _ := HostManager.FetchByIdOrName(ctx, nil, query.HostId)
  118. if host == nil {
  119. return nil, httperrors.NewResourceNotFoundError("host %s not found", query.HostId)
  120. }
  121. gst := GuestManager.Query().SubQuery()
  122. q = q.Join(gst, sqlchemy.Equals(q.Field("guest_id"), gst.Field("id")))
  123. q = q.Filter(sqlchemy.Equals(gst.Field("host_id"), host.GetId()))
  124. }
  125. return q, nil
  126. }
  127. func (m *SContainerManager) GetContainersByPod(guestId string) ([]SContainer, error) {
  128. q := m.Query().Equals("guest_id", guestId).Asc("created_at")
  129. ctrs := make([]SContainer, 0)
  130. if err := db.FetchModelObjects(m, q, &ctrs); err != nil {
  131. return nil, errors.Wrap(err, "db.FetchModelObjects")
  132. }
  133. return ctrs, nil
  134. }
  135. func (m *SContainerManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, _ jsonutils.JSONObject, input *api.ContainerCreateInput) (*api.ContainerCreateInput, error) {
  136. if input.GuestId == "" {
  137. return nil, httperrors.NewNotEmptyError("guest_id is required")
  138. }
  139. obj, err := GuestManager.FetchByIdOrName(ctx, userCred, input.GuestId)
  140. if err != nil {
  141. return nil, errors.Wrapf(err, "fetch guest by %s", input.GuestId)
  142. }
  143. pod := obj.(*SGuest)
  144. input.GuestId = pod.GetId()
  145. if err := m.ValidateSpec(ctx, userCred, &input.Spec, pod, nil); err != nil {
  146. return nil, errors.Wrap(err, "validate spec")
  147. }
  148. return input, nil
  149. }
  150. func (m *SContainerManager) ValidateSpec(ctx context.Context, userCred mcclient.TokenCredential, spec *api.ContainerSpec, pod *SGuest, ctr *SContainer) error {
  151. if spec.ImagePullPolicy == "" {
  152. spec.ImagePullPolicy = apis.ImagePullPolicyIfNotPresent
  153. }
  154. if !sets.NewString(apis.ImagePullPolicyAlways, apis.ImagePullPolicyIfNotPresent).Has(string(spec.ImagePullPolicy)) {
  155. return httperrors.NewInputParameterError("invalid image_pull_policy %s", spec.ImagePullPolicy)
  156. }
  157. if spec.ImageCredentialId != "" {
  158. if _, err := m.GetImageCredential(ctx, userCred, spec.ImageCredentialId); err != nil {
  159. return errors.Wrapf(err, "get image credential by id: %s", spec.ImageCredentialId)
  160. }
  161. }
  162. if err := m.ValidateSpecEnvs(ctx, userCred, spec); err != nil {
  163. return errors.Wrap(err, "validate envs")
  164. }
  165. if pod != nil {
  166. if err := m.ValidateSpecRootFs(ctx, userCred, pod, spec, ctr); err != nil {
  167. return errors.Wrap(err, "ValidateSpecRootFs")
  168. }
  169. if err := m.ValidateSpecVolumeMounts(ctx, userCred, pod, spec, ctr); err != nil {
  170. return errors.Wrap(err, "ValidateSpecVolumeMounts")
  171. }
  172. for idx, dev := range spec.Devices {
  173. newDev, err := m.ValidateSpecDevice(ctx, userCred, pod, dev)
  174. if err != nil {
  175. return errors.Wrapf(err, "validate device %s", jsonutils.Marshal(dev))
  176. }
  177. spec.Devices[idx] = newDev
  178. }
  179. }
  180. if err := m.ValidateSpecLifecycle(ctx, userCred, spec); err != nil {
  181. return errors.Wrap(err, "validate lifecycle")
  182. }
  183. if spec.ShmSizeMB != 0 && spec.ShmSizeMB < 64 {
  184. return httperrors.NewInputParameterError("/dev/shm size is small than 64MB")
  185. }
  186. if err := m.ValidateSpecProbe(ctx, userCred, spec); err != nil {
  187. return errors.Wrap(err, "validate probe configuration")
  188. }
  189. if ctr != nil {
  190. // only detect loop when update container
  191. ctrs, err := m.GetContainersByPod(pod.GetId())
  192. if err != nil {
  193. return errors.Wrap(err, "get containers by pod")
  194. }
  195. for idx, container := range ctrs {
  196. if container.GetId() == ctr.GetId() {
  197. ctrs[idx].Spec = spec
  198. }
  199. }
  200. err = utils.TopologicalSortContainers(
  201. ctrs,
  202. func(ctr SContainer) string { return ctr.Name },
  203. func(ctr SContainer) []string { return ctr.Spec.DependsOn },
  204. )
  205. if err != nil {
  206. return errors.Wrap(err, "validate topological sort")
  207. }
  208. }
  209. return nil
  210. }
  211. func (m *SContainerManager) ValidateSpecEnvs(ctx context.Context, userCred mcclient.TokenCredential, spec *api.ContainerSpec) error {
  212. var errs []error
  213. for _, env := range spec.Envs {
  214. if env.ValueFrom == nil {
  215. continue
  216. }
  217. if env.ValueFrom.Credential != nil {
  218. credId := env.ValueFrom.Credential.Id
  219. if credId == "" {
  220. errs = append(errs, errors.Wrapf(errors.ErrEmpty, "credential id is empty"))
  221. continue
  222. }
  223. credKey := env.ValueFrom.Credential.Key
  224. if credKey == "" {
  225. errs = append(errs, errors.Wrapf(errors.ErrEmpty, "credential key is empty"))
  226. continue
  227. }
  228. cred, err := m.GetSecretCredential(ctx, userCred, credId)
  229. if err != nil {
  230. errs = append(errs, errors.Wrapf(err, "get secret credential %s", credId))
  231. }
  232. _, ok := cred[credKey]
  233. if !ok {
  234. errs = append(errs, errors.Wrapf(errors.ErrNotFound, "env %s secret credential %s key %s not found", env.Key, credId, credKey))
  235. }
  236. }
  237. }
  238. return errors.NewAggregate(errs)
  239. }
  240. func (m *SContainerManager) ValidateSpecLifecycle(ctx context.Context, cred mcclient.TokenCredential, spec *api.ContainerSpec) error {
  241. if spec.Lifecyle == nil {
  242. return nil
  243. }
  244. if err := m.ValidateSpecLifecyclePostStart(ctx, cred, spec.Lifecyle.PostStart); err != nil {
  245. return errors.Wrap(err, "validate post start")
  246. }
  247. return nil
  248. }
  249. func (m *SContainerManager) ValidateSpecLifecyclePostStart(ctx context.Context, userCred mcclient.TokenCredential, input *apis.ContainerLifecyleHandler) error {
  250. drv, err := GetContainerLifecyleDriverWithError(input.Type)
  251. if err != nil {
  252. return httperrors.NewInputParameterError("get lifecycle driver: %v", err)
  253. }
  254. return drv.ValidateCreateData(ctx, userCred, input)
  255. }
  256. func (m *SContainerManager) ValidateSpecDevice(ctx context.Context, userCred mcclient.TokenCredential, pod *SGuest, dev *api.ContainerDevice) (*api.ContainerDevice, error) {
  257. drv, err := GetContainerDeviceDriverWithError(dev.Type)
  258. if err != nil {
  259. return nil, httperrors.NewInputParameterError("get device driver: %v", err)
  260. }
  261. return drv.ValidateCreateData(ctx, userCred, pod, dev)
  262. }
  263. func (m *SContainerManager) ValidateSpecRootFs(ctx context.Context, userCred mcclient.TokenCredential, pod *SGuest, spec *api.ContainerSpec, ctr *SContainer) error {
  264. if spec.RootFs == nil {
  265. return nil
  266. }
  267. rootFs := spec.RootFs
  268. if rootFs.Disk != nil {
  269. if rootFs.Disk.SubDirectory == "" {
  270. rootFs.Disk.SubDirectory = "rootfs"
  271. }
  272. }
  273. drv, err := GetContainerRootFsDriverWithError(rootFs.Type)
  274. if err != nil {
  275. return errors.Wrapf(err, "get container volume mount driver %q", rootFs.Type)
  276. }
  277. if err := drv.ValidateRootFsCreateData(ctx, userCred, pod, rootFs); err != nil {
  278. return errors.Wrapf(err, "validate %s create data", drv.GetType())
  279. }
  280. return nil
  281. }
  282. func (m *SContainerManager) ValidateSpecVolumeMounts(ctx context.Context, userCred mcclient.TokenCredential, pod *SGuest, spec *api.ContainerSpec, ctr *SContainer) error {
  283. relation, err := m.GetVolumeMountRelations(pod, spec)
  284. if err != nil {
  285. return errors.Wrap(err, "GetVolumeMountRelations")
  286. }
  287. curCtrs, _ := m.GetContainersByPod(pod.GetId())
  288. volUniqNames := sets.NewString()
  289. for idx := range curCtrs {
  290. if ctr != nil && ctr.GetId() == curCtrs[idx].GetId() {
  291. continue
  292. }
  293. for _, vol := range curCtrs[idx].Spec.VolumeMounts {
  294. if vol.UniqueName != "" {
  295. volUniqNames.Insert(vol.UniqueName)
  296. }
  297. }
  298. }
  299. for idx, vm := range spec.VolumeMounts {
  300. if vm.UniqueName != "" {
  301. if volUniqNames.Has(vm.UniqueName) {
  302. return httperrors.NewDuplicateNameError("volume_mount unique_name %s", fmt.Sprintf("%s: %s, index: %d", vm.UniqueName, jsonutils.Marshal(vm), idx))
  303. } else {
  304. volUniqNames.Insert(vm.UniqueName)
  305. }
  306. }
  307. newVm, err := m.ValidateSpecVolumeMount(ctx, userCred, pod, vm)
  308. if err != nil {
  309. return errors.Wrapf(err, "validate volume mount %s", jsonutils.Marshal(vm))
  310. }
  311. spec.VolumeMounts[idx] = newVm
  312. }
  313. if _, err := m.ConvertVolumeMountRelationToSpec(ctx, userCred, relation); err != nil {
  314. return errors.Wrap(err, "ConvertVolumeMountRelationToSpec")
  315. }
  316. return nil
  317. }
  318. func (m *SContainerManager) ValidateSpecVolumeMount(ctx context.Context, userCred mcclient.TokenCredential, pod *SGuest, vm *apis.ContainerVolumeMount) (*apis.ContainerVolumeMount, error) {
  319. if vm.Type == "" {
  320. return nil, httperrors.NewNotEmptyError("type is required")
  321. }
  322. if vm.MountPath == "" {
  323. return nil, httperrors.NewNotEmptyError("mount_path is required")
  324. }
  325. drv, err := GetContainerVolumeMountDriverWithError(vm.Type)
  326. if err != nil {
  327. return nil, errors.Wrapf(err, "get container volume mount driver %s", vm.Type)
  328. }
  329. vm, err = drv.ValidateCreateData(ctx, userCred, pod, vm)
  330. if err != nil {
  331. return nil, errors.Wrapf(err, "validate %s create data", drv.GetType())
  332. }
  333. return vm, nil
  334. }
  335. /*func (m *SContainerManager) GetContainerIndex(guestId string) (int, error) {
  336. cnt, err := m.Query("guest_id").Equals("guest_id", guestId).CountWithError()
  337. if err != nil {
  338. return -1, errors.Wrapf(err, "get container numbers of pod %s", guestId)
  339. }
  340. return cnt, nil
  341. }
  342. func (c *SContainer) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
  343. input := new(api.ContainerCreateInput)
  344. if err := data.Unmarshal(input); err != nil {
  345. return errors.Wrap(err, "unmarshal to ContainerCreateInput")
  346. }
  347. if input.Spec.ImagePullPolicy == "" {
  348. c.Spec.ImagePullPolicy = apis.ImagePullPolicyIfNotPresent
  349. }
  350. return nil
  351. }*/
  352. func (m *SContainerManager) ValidateSpecProbe(ctx context.Context, userCred mcclient.TokenCredential, spec *api.ContainerSpec) error {
  353. //if err := m.validateSpecProbe(ctx, userCred, spec.LivenessProbe); err != nil {
  354. // return errors.Wrap(err, "validate liveness probe")
  355. //}
  356. if err := m.validateSpecProbe(ctx, userCred, spec.StartupProbe); err != nil {
  357. return errors.Wrap(err, "validate startup probe")
  358. }
  359. return nil
  360. }
  361. func (m *SContainerManager) validateSpecProbe(ctx context.Context, userCred mcclient.TokenCredential, probe *apis.ContainerProbe) error {
  362. if probe == nil {
  363. return nil
  364. }
  365. if err := m.validateSpecProbeHandler(probe.ContainerProbeHandler); err != nil {
  366. return errors.Wrap(err, "validate container probe handler")
  367. }
  368. for key, val := range map[string]int32{
  369. //"initial_delay_seconds": probe.InitialDelaySeconds,
  370. "timeout_seconds": probe.TimeoutSeconds,
  371. "period_seconds": probe.PeriodSeconds,
  372. "success_threshold": probe.SuccessThreshold,
  373. "failure_threshold": probe.FailureThreshold,
  374. } {
  375. if val < 0 {
  376. return httperrors.NewInputParameterError("%s is negative", key)
  377. }
  378. }
  379. //if probe.InitialDelaySeconds == 0 {
  380. // probe.InitialDelaySeconds = 5
  381. //}
  382. if probe.TimeoutSeconds == 0 {
  383. probe.TimeoutSeconds = 3
  384. }
  385. if probe.PeriodSeconds == 0 {
  386. probe.PeriodSeconds = 10
  387. }
  388. if probe.SuccessThreshold == 0 {
  389. probe.SuccessThreshold = 1
  390. }
  391. if probe.FailureThreshold == 0 {
  392. probe.FailureThreshold = 3
  393. }
  394. return nil
  395. }
  396. func (m *SContainerManager) validateSpecProbeHandler(probe apis.ContainerProbeHandler) error {
  397. isAllNil := true
  398. if probe.Exec != nil {
  399. isAllNil = false
  400. if len(probe.Exec.Command) == 0 {
  401. return httperrors.NewInputParameterError("exec command is required")
  402. }
  403. }
  404. if probe.TCPSocket != nil {
  405. isAllNil = false
  406. port := probe.TCPSocket.Port
  407. if port < 1 || port > 65535 {
  408. return httperrors.NewInputParameterError("invalid tcp socket port: %d, must between [1,65535]", port)
  409. }
  410. }
  411. if probe.HTTPGet != nil {
  412. isAllNil = false
  413. port := probe.HTTPGet.Port
  414. if port < 1 || port > 65535 {
  415. return httperrors.NewInputParameterError("invalid http port: %d, must between [1,65535]", port)
  416. }
  417. }
  418. if isAllNil {
  419. return httperrors.NewInputParameterError("one of [exec, http_get, tcp_socket] is required")
  420. }
  421. return nil
  422. }
  423. func (m *SContainerManager) startBatchTask(ctx context.Context, userCred mcclient.TokenCredential, taskName string, ctrs []SContainer, taskData *jsonutils.JSONDict, parentTaskId string) error {
  424. ctrPtrs := make([]db.IStandaloneModel, len(ctrs))
  425. for i := range ctrs {
  426. ctrPtrs[i] = &ctrs[i]
  427. }
  428. task, err := taskman.TaskManager.NewParallelTask(ctx, taskName, ctrPtrs, userCred, taskData, parentTaskId, "")
  429. if err != nil {
  430. return errors.Wrapf(err, "NewParallelTask %s", taskName)
  431. }
  432. return task.ScheduleRun(nil)
  433. }
  434. func (m *SContainerManager) StartBatchStartTask(ctx context.Context, userCred mcclient.TokenCredential, ctrs []SContainer, parentTaskId string) error {
  435. return m.startBatchTask(ctx, userCred, "ContainerBatchStartTask", ctrs, nil, parentTaskId)
  436. }
  437. func (m *SContainerManager) StartBatchStopTask(ctx context.Context, userCred mcclient.TokenCredential, ctrs []SContainer, timeout int, force bool, parentTaskId string) error {
  438. params := make([]api.ContainerStopInput, len(ctrs))
  439. for i := range ctrs {
  440. params[i] = api.ContainerStopInput{
  441. Timeout: timeout,
  442. Force: force,
  443. }
  444. }
  445. taskParams := jsonutils.NewDict()
  446. taskParams.Add(jsonutils.Marshal(params), "params")
  447. return m.startBatchTask(ctx, userCred, "ContainerBatchStopTask", ctrs, taskParams, parentTaskId)
  448. }
  449. func (m *SContainerManager) StartBatchRestartTask(ctx context.Context, userCred mcclient.TokenCredential, ctrs []SContainer, timeout int, force bool, parentTaskId string) error {
  450. params := make([]api.ContainerRestartInput, len(ctrs))
  451. for i := range ctrs {
  452. params[i] = api.ContainerRestartInput{
  453. Timeout: timeout,
  454. Force: force,
  455. }
  456. }
  457. taskParams := jsonutils.NewDict()
  458. taskParams.Add(jsonutils.Marshal(params), "params")
  459. return m.startBatchTask(ctx, userCred, "ContainerBatchRestartTask", ctrs, taskParams, parentTaskId)
  460. }
  461. func (c *SContainer) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
  462. c.SVirtualResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
  463. if !jsonutils.QueryBoolean(data, "skip_task", false) {
  464. if err := c.StartCreateTask(ctx, userCred, "", data.(*jsonutils.JSONDict)); err != nil {
  465. log.Errorf("StartCreateTask error: %v", err)
  466. }
  467. }
  468. }
  469. func (c *SContainer) StartCreateTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string, params *jsonutils.JSONDict) error {
  470. task, err := taskman.TaskManager.NewTask(ctx, "ContainerCreateTask", c, userCred, params, parentTaskId, "", nil)
  471. if err != nil {
  472. return errors.Wrap(err, "NewTask")
  473. }
  474. return task.ScheduleRun(nil)
  475. }
  476. // func (c *SContainer) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.ContainerUpdateInput) (*api.ContainerUpdateInput, error) {
  477. func (c *SContainer) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (*api.ContainerUpdateInput, error) {
  478. if !api.ContainerExitedStatus.Has(c.GetStatus()) {
  479. return nil, httperrors.NewInvalidStatusError("current status %s is not in %v", c.GetStatus(), api.ContainerExitedStatus.List())
  480. }
  481. input := new(api.ContainerUpdateInput)
  482. if err := data.Unmarshal(input); err != nil {
  483. return nil, errors.Wrap(err, "Unmarshal")
  484. }
  485. baseInput, err := c.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input.VirtualResourceBaseUpdateInput)
  486. if err != nil {
  487. return input, errors.Wrap(err, "SVirtualResourceBase.ValidateUpdateData")
  488. }
  489. input.VirtualResourceBaseUpdateInput = baseInput
  490. if err := GetContainerManager().ValidateSpec(ctx, userCred, &input.Spec, c.GetPod(), c); err != nil {
  491. return nil, errors.Wrap(err, "validate spec")
  492. }
  493. return input, nil
  494. }
  495. func (c *SContainer) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) {
  496. c.SVirtualResourceBase.PostUpdate(ctx, userCred, query, data)
  497. if err := c.GetPod().StartSyncTaskWithoutSyncstatus(ctx, userCred, false, ""); err != nil {
  498. log.Errorf("container %s StartSyncTaskWithoutSyncstatus error: %v", c.GetName(), err)
  499. }
  500. }
  501. func (c *SContainer) GetPod() *SGuest {
  502. return GuestManager.FetchGuestById(c.GuestId)
  503. }
  504. func (c *SContainer) GetVolumeMounts() []*apis.ContainerVolumeMount {
  505. return c.Spec.VolumeMounts
  506. }
  507. type ContainerRootFsRelation struct {
  508. RootFs *apis.ContainerRootfs
  509. pod *SGuest
  510. }
  511. func (rr *ContainerRootFsRelation) ToHostRootFs() (*hostapi.ContainerRootfs, error) {
  512. drv, err := GetContainerRootFsDriverWithError(rr.RootFs.Type)
  513. if err != nil {
  514. return nil, errors.Wrapf(err, "get container root fs driver %q", rr.RootFs.Type)
  515. }
  516. rootFs, err := drv.ToHostRootFs(rr.RootFs)
  517. if err != nil {
  518. return nil, errors.Wrapf(err, "to host root fs")
  519. }
  520. return rootFs, nil
  521. }
  522. type ContainerVolumeMountRelation struct {
  523. VolumeMount *apis.ContainerVolumeMount
  524. pod *SGuest
  525. }
  526. func (vm *ContainerVolumeMountRelation) toHostDiskMount(disk *apis.ContainerVolumeMountDisk) (*hostapi.ContainerVolumeMountDisk, error) {
  527. diskObj := DiskManager.FetchDiskById(disk.Id)
  528. if diskObj == nil {
  529. return nil, errors.Errorf("fetch disk by id %s", disk.Id)
  530. }
  531. ret := &hostapi.ContainerVolumeMountDisk{
  532. Index: disk.Index,
  533. Id: disk.Id,
  534. TemplateId: diskObj.TemplateId,
  535. SubDirectory: disk.SubDirectory,
  536. StorageSizeFile: disk.StorageSizeFile,
  537. Overlay: disk.Overlay,
  538. CaseInsensitivePaths: disk.CaseInsensitivePaths,
  539. PostOverlay: disk.PostOverlay,
  540. ResUid: disk.ResUid,
  541. ResGid: disk.ResGid,
  542. }
  543. return ret, nil
  544. }
  545. func (vm *ContainerVolumeMountRelation) toCephFSMount(fs *apis.ContainerVolumeMountCephFS) (*hostapi.ContainerVolumeMountCephFS, error) {
  546. fsObj, err := FileSystemManager.FetchById(fs.Id)
  547. if err != nil {
  548. return nil, errors.Errorf("fetch cephfs by id %s", fs.Id)
  549. }
  550. filesystem := fsObj.(*SFileSystem)
  551. ret := &hostapi.ContainerVolumeMountCephFS{
  552. Id: filesystem.Id,
  553. Path: filesystem.ExternalId,
  554. }
  555. account := filesystem.GetCloudaccount()
  556. if gotypes.IsNil(account) {
  557. return nil, fmt.Errorf("invalid cephfs %s", filesystem.Name)
  558. }
  559. ret.Secret, err = account.GetOptionPassword()
  560. if err != nil {
  561. return nil, err
  562. }
  563. ret.Name, _ = account.Options.GetString("name")
  564. if len(ret.Name) == 0 {
  565. ret.Name = "admin"
  566. }
  567. ret.MonHost, _ = account.Options.GetString("mon_host")
  568. return ret, nil
  569. }
  570. func (vm *ContainerVolumeMountRelation) ToHostMount(ctx context.Context, userCred mcclient.TokenCredential) (*hostapi.ContainerVolumeMount, error) {
  571. ret := &hostapi.ContainerVolumeMount{
  572. UniqueName: vm.VolumeMount.UniqueName,
  573. Type: vm.VolumeMount.Type,
  574. Disk: nil,
  575. CephFS: nil,
  576. Text: vm.VolumeMount.Text,
  577. HostPath: vm.VolumeMount.HostPath,
  578. ReadOnly: vm.VolumeMount.ReadOnly,
  579. MountPath: vm.VolumeMount.MountPath,
  580. SelinuxRelabel: vm.VolumeMount.SelinuxRelabel,
  581. Propagation: vm.VolumeMount.Propagation,
  582. FsUser: vm.VolumeMount.FsUser,
  583. FsGroup: vm.VolumeMount.FsGroup,
  584. }
  585. if vm.VolumeMount.Disk != nil {
  586. disk, err := vm.toHostDiskMount(vm.VolumeMount.Disk)
  587. if err != nil {
  588. return nil, errors.Wrap(err, "toHostDiskMount")
  589. }
  590. ret.Disk = disk
  591. }
  592. if vm.VolumeMount.CephFS != nil {
  593. fs, err := vm.toCephFSMount(vm.VolumeMount.CephFS)
  594. if err != nil {
  595. return nil, errors.Wrapf(err, "getCephFSSecret")
  596. }
  597. ret.CephFS = fs
  598. }
  599. return ret, nil
  600. }
  601. func (m *SContainerManager) GetRootFsRelation(pod *SGuest, spec *api.ContainerSpec) (*ContainerRootFsRelation, error) {
  602. return &ContainerRootFsRelation{
  603. RootFs: spec.RootFs,
  604. pod: pod,
  605. }, nil
  606. }
  607. func (m *SContainerManager) GetVolumeMountRelations(pod *SGuest, spec *api.ContainerSpec) ([]*ContainerVolumeMountRelation, error) {
  608. relation := make([]*ContainerVolumeMountRelation, len(spec.VolumeMounts))
  609. for idx, vm := range spec.VolumeMounts {
  610. tmpVm := vm
  611. relation[idx] = &ContainerVolumeMountRelation{
  612. VolumeMount: tmpVm,
  613. pod: pod,
  614. }
  615. }
  616. return relation, nil
  617. }
  618. func (c *SContainer) GetRootFsRelation() (*ContainerRootFsRelation, error) {
  619. return GetContainerManager().GetRootFsRelation(c.GetPod(), c.Spec)
  620. }
  621. func (c *SContainer) GetVolumeMountRelations() ([]*ContainerVolumeMountRelation, error) {
  622. return GetContainerManager().GetVolumeMountRelations(c.GetPod(), c.Spec)
  623. }
  624. func (c *SContainer) PerformStart(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  625. if !sets.NewString(api.CONTAINER_STATUS_EXITED, api.CONTAINER_STATUS_START_FAILED).Has(c.Status) {
  626. return nil, httperrors.NewInvalidStatusError("Can't start container in status %s", c.Status)
  627. }
  628. return nil, c.StartStartTask(ctx, userCred, "")
  629. }
  630. func (c *SContainer) StartStartTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
  631. c.SetStatus(ctx, userCred, api.CONTAINER_STATUS_STARTING, "")
  632. task, err := taskman.TaskManager.NewTask(ctx, "ContainerStartTask", c, userCred, nil, parentTaskId, "", nil)
  633. if err != nil {
  634. return errors.Wrap(err, "NewTask")
  635. }
  636. return task.ScheduleRun(nil)
  637. }
  638. func (c *SContainer) PerformStop(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *api.ContainerStopInput) (jsonutils.JSONObject, error) {
  639. if !data.Force {
  640. if !sets.NewString(
  641. api.CONTAINER_STATUS_RUNNING,
  642. api.CONTAINER_STATUS_PROBING,
  643. api.CONTAINER_STATUS_PROBE_FAILED,
  644. api.CONTAINER_STATUS_STOP_FAILED).Has(c.Status) {
  645. return nil, httperrors.NewInvalidStatusError("Can't stop container in status %s", c.Status)
  646. }
  647. }
  648. return nil, c.StartStopTask(ctx, userCred, data, "")
  649. }
  650. func (c *SContainer) StartStopTask(ctx context.Context, userCred mcclient.TokenCredential, data *api.ContainerStopInput, parentTaskId string) error {
  651. c.SetStatus(ctx, userCred, api.CONTAINER_STATUS_STOPPING, "")
  652. task, err := taskman.TaskManager.NewTask(ctx, "ContainerStopTask", c, userCred, jsonutils.Marshal(data).(*jsonutils.JSONDict), parentTaskId, "", nil)
  653. if err != nil {
  654. return errors.Wrap(err, "NewTask")
  655. }
  656. return task.ScheduleRun(nil)
  657. }
  658. func (c *SContainer) PerformRestart(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *api.ContainerRestartInput) (jsonutils.JSONObject, error) {
  659. if !data.Force {
  660. if !sets.NewString(
  661. api.CONTAINER_STATUS_RUNNING,
  662. api.CONTAINER_STATUS_PROBING,
  663. api.CONTAINER_STATUS_PROBE_FAILED,
  664. api.CONTAINER_STATUS_STOP_FAILED).Has(c.Status) {
  665. return nil, httperrors.NewInvalidStatusError("Can't restart container in status %s", c.Status)
  666. }
  667. }
  668. return nil, c.StartRestartTask(ctx, userCred, data, "")
  669. }
  670. func (c *SContainer) StartRestartTask(ctx context.Context, userCred mcclient.TokenCredential, data *api.ContainerRestartInput, parentTaskId string) error {
  671. task, err := taskman.TaskManager.NewTask(ctx, "ContainerRestartTask", c, userCred, jsonutils.Marshal(data).(*jsonutils.JSONDict), parentTaskId, "", nil)
  672. if err != nil {
  673. return errors.Wrap(err, "NewTask")
  674. }
  675. return task.ScheduleRun(nil)
  676. }
  677. func (c *SContainer) PerformSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
  678. return nil, c.StartSyncStatusTask(ctx, userCred, "")
  679. }
  680. func (c *SContainer) StartSyncStatusTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
  681. c.SetStatus(ctx, userCred, api.CONTAINER_STATUS_SYNC_STATUS, "")
  682. task, err := taskman.TaskManager.NewTask(ctx, "ContainerSyncStatusTask", c, userCred, nil, parentTaskId, "", nil)
  683. if err != nil {
  684. return errors.Wrap(err, "NewTask")
  685. }
  686. return task.ScheduleRun(nil)
  687. }
  688. func (c *SContainer) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query, data jsonutils.JSONObject) error {
  689. return c.StartDeleteTask(ctx, userCred, "", jsonutils.QueryBoolean(data, "purge", false))
  690. }
  691. func (c *SContainer) StartDeleteTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string, purge bool) error {
  692. c.SetStatus(ctx, userCred, api.CONTAINER_STATUS_DELETING, "")
  693. task, err := taskman.TaskManager.NewTask(ctx, "ContainerDeleteTask", c, userCred, jsonutils.Marshal(map[string]interface{}{
  694. "purge": purge,
  695. }).(*jsonutils.JSONDict), parentTaskId, "", nil)
  696. if err != nil {
  697. return errors.Wrap(err, "NewTask")
  698. }
  699. return task.ScheduleRun(nil)
  700. }
  701. func (m *SContainerManager) getKeystoneCredential(ctx context.Context, userCred mcclient.TokenCredential, id string) (jsonutils.JSONObject, error) {
  702. s := auth.GetSession(ctx, userCred, options.Options.Region)
  703. if cred, err := identitymod.Credentials.GetById(s, id, nil); err == nil {
  704. return cred, nil
  705. } else if errors.Cause(err) == errors.ErrNotFound || strings.Contains(err.Error(), "NotFound") {
  706. cred2, err2 := identitymod.Credentials.GetByName(s, id, nil)
  707. if err2 != nil {
  708. return nil, errors.Wrapf(err2, "get credential by id or name of %s", id)
  709. }
  710. return cred2, nil
  711. } else {
  712. return nil, errors.Wrapf(err, "get credentials by id with %s", userCred.String())
  713. }
  714. }
  715. func (m *SContainerManager) parseKeystoneCredentialBlob(ret jsonutils.JSONObject, expectedType string) (jsonutils.JSONObject, error) {
  716. credType, _ := ret.GetString("type")
  717. if credType != expectedType {
  718. return nil, httperrors.NewNotSupportedError("unsupported credential type %s", credType)
  719. }
  720. blobStr, err := ret.GetString("blob")
  721. if err != nil {
  722. return nil, errors.Wrap(err, "get blob")
  723. }
  724. obj, err := jsonutils.ParseString(blobStr)
  725. if err != nil {
  726. return nil, errors.Wrapf(err, "json parse string: %s", blobStr)
  727. }
  728. return obj, nil
  729. }
  730. func (m *SContainerManager) GetImageCredential(ctx context.Context, userCred mcclient.TokenCredential, id string) (*apis.ContainerPullImageAuthConfig, error) {
  731. ret, err := m.getKeystoneCredential(ctx, userCred, id)
  732. if err != nil {
  733. return nil, err
  734. }
  735. obj, err := m.parseKeystoneCredentialBlob(ret, identityapi.CONTAINER_IMAGE_TYPE)
  736. if err != nil {
  737. return nil, err
  738. }
  739. blob := new(identityapi.CredentialContainerImageBlob)
  740. if err := obj.Unmarshal(blob); err != nil {
  741. return nil, errors.Wrap(err, "unmarshal blob")
  742. }
  743. out := &apis.ContainerPullImageAuthConfig{
  744. Username: blob.Username,
  745. Password: blob.Password,
  746. Auth: blob.Auth,
  747. ServerAddress: blob.ServerAddress,
  748. IdentityToken: blob.IdentityToken,
  749. RegistryToken: blob.RegistryToken,
  750. }
  751. return out, nil
  752. }
  753. func (m *SContainerManager) GetSecretCredential(ctx context.Context, userCred mcclient.TokenCredential, id string) (map[string]string, error) {
  754. ret, err := m.getKeystoneCredential(ctx, userCred, id)
  755. if err != nil {
  756. return nil, err
  757. }
  758. obj, err := m.parseKeystoneCredentialBlob(ret, identityapi.CONTAINER_SECRET_TYPE)
  759. if err != nil {
  760. return nil, err
  761. }
  762. out := map[string]string{}
  763. if err := obj.Unmarshal(&out); err != nil {
  764. return nil, errors.Wrap(err, "unmarshal blob")
  765. }
  766. return out, nil
  767. }
  768. func (c *SContainer) GetImageCredential(ctx context.Context, userCred mcclient.TokenCredential) (*apis.ContainerPullImageAuthConfig, error) {
  769. if c.Spec.ImageCredentialId == "" {
  770. return nil, errors.Wrap(errors.ErrEmpty, "image_credential_id is empty")
  771. }
  772. return GetContainerManager().GetImageCredential(ctx, userCred, c.Spec.ImageCredentialId)
  773. }
  774. func (c *SContainer) GetHostPullImageInput(ctx context.Context, userCred mcclient.TokenCredential) (*hostapi.ContainerPullImageInput, error) {
  775. input := &hostapi.ContainerPullImageInput{
  776. Image: c.Spec.Image,
  777. PullPolicy: c.Spec.ImagePullPolicy,
  778. }
  779. if c.Spec.ImageCredentialId != "" {
  780. cred, err := c.GetImageCredential(ctx, userCred)
  781. if err != nil {
  782. return nil, errors.Wrapf(err, "GetImageCredential %s", c.Spec.ImageCredentialId)
  783. }
  784. input.Auth = cred
  785. }
  786. return input, nil
  787. }
  788. func (c *SContainer) GetSecretCredentials(ctx context.Context, userCred mcclient.TokenCredential) (map[string]string, error) {
  789. ret := make(map[string]string, 0)
  790. for _, env := range c.Spec.Envs {
  791. if env.ValueFrom == nil {
  792. continue
  793. }
  794. if env.ValueFrom.Credential != nil {
  795. credId := env.ValueFrom.Credential.Id
  796. cred, err := GetContainerManager().GetSecretCredential(ctx, userCred, credId)
  797. if err != nil {
  798. return nil, errors.Wrapf(err, "GetSecretCredential %s", credId)
  799. }
  800. ret[credId] = base64.StdEncoding.EncodeToString([]byte(jsonutils.Marshal(cred).String()))
  801. }
  802. }
  803. return ret, nil
  804. }
  805. func (c *SContainer) StartPullImageTask(ctx context.Context, userCred mcclient.TokenCredential, input *hostapi.ContainerPullImageInput, parentTaskId string) error {
  806. c.SetStatus(ctx, userCred, api.CONTAINER_STATUS_PULLING_IMAGE, "")
  807. task, err := taskman.TaskManager.NewTask(ctx, "ContainerPullImageTask", c, userCred, jsonutils.Marshal(input).(*jsonutils.JSONDict), parentTaskId, "", nil)
  808. if err != nil {
  809. return errors.Wrap(err, "NewTask")
  810. }
  811. return task.ScheduleRun(nil)
  812. }
  813. func (c *SContainer) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
  814. return c.SVirtualResourceBase.Delete(ctx, userCred)
  815. }
  816. func (m *SContainerManager) ConvertVolumeMountRelationToSpec(ctx context.Context, userCred mcclient.TokenCredential, relation []*ContainerVolumeMountRelation) ([]*hostapi.ContainerVolumeMount, error) {
  817. mounts := make([]*hostapi.ContainerVolumeMount, 0)
  818. for _, r := range relation {
  819. mount, err := r.ToHostMount(ctx, userCred)
  820. if err != nil {
  821. return nil, errors.Wrapf(err, "ToMountOrDevice: %#v", r)
  822. }
  823. if mount != nil {
  824. mounts = append(mounts, mount)
  825. }
  826. }
  827. return mounts, nil
  828. }
  829. func (m *SContainerManager) ConvertRootFsRelationToSpec(ctx context.Context, userCred mcclient.TokenCredential, relation *ContainerRootFsRelation) (*hostapi.ContainerRootfs, error) {
  830. if relation.RootFs == nil {
  831. return nil, nil
  832. }
  833. rootFs, err := relation.ToHostRootFs()
  834. if err != nil {
  835. return nil, errors.Wrap(err, "ToHostRootFs")
  836. }
  837. return rootFs, nil
  838. }
  839. func (c *SContainer) ToHostContainerSpec(ctx context.Context, userCred mcclient.TokenCredential) (*hostapi.ContainerSpec, error) {
  840. rootFsRelation, err := c.GetRootFsRelation()
  841. if err != nil {
  842. return nil, errors.Wrap(err, "GetRootFsRelation")
  843. }
  844. rootFs, err := GetContainerManager().ConvertRootFsRelationToSpec(ctx, userCred, rootFsRelation)
  845. if err != nil {
  846. return nil, errors.Wrap(err, "ConvertRootFsRelationToSpec")
  847. }
  848. vmRelation, err := c.GetVolumeMountRelations()
  849. if err != nil {
  850. return nil, errors.Wrap(err, "GetVolumeMountRelations")
  851. }
  852. mounts, err := GetContainerManager().ConvertVolumeMountRelationToSpec(ctx, userCred, vmRelation)
  853. if err != nil {
  854. return nil, errors.Wrap(err, "ConvertVolumeRelationToSpec")
  855. }
  856. ctrDevs := make([]*hostapi.ContainerDevice, 0)
  857. for _, dev := range c.Spec.Devices {
  858. ctrDev, err := GetContainerDeviceDriver(dev.Type).ToHostDevice(dev)
  859. if err != nil {
  860. return nil, errors.Wrapf(err, "ToHostDevice %s", jsonutils.Marshal(dev))
  861. }
  862. ctrDevs = append(ctrDevs, ctrDev)
  863. }
  864. spec := c.Spec.ContainerSpec
  865. hSpec := &hostapi.ContainerSpec{
  866. ContainerSpec: spec,
  867. Rootfs: rootFs,
  868. VolumeMounts: mounts,
  869. Devices: ctrDevs,
  870. }
  871. pullInput, err := c.GetHostPullImageInput(ctx, userCred)
  872. if err != nil {
  873. return nil, errors.Wrap(err, "GetHostPullImageInput")
  874. }
  875. hSpec.ImageCredentialToken = base64.StdEncoding.EncodeToString([]byte(jsonutils.Marshal(pullInput.Auth).String()))
  876. secretCredentials, err := c.GetSecretCredentials(ctx, userCred)
  877. if err != nil {
  878. return nil, errors.Wrap(err, "GetSecretCredentials")
  879. }
  880. hSpec.SecretCredentials = secretCredentials
  881. return hSpec, nil
  882. }
  883. func (c *SContainer) GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential) (*hostapi.ContainerDesc, error) {
  884. spec, err := c.ToHostContainerSpec(ctx, userCred)
  885. if err != nil {
  886. return nil, errors.Wrap(err, "ToHostContainerSpec")
  887. }
  888. return &hostapi.ContainerDesc{
  889. Id: c.GetId(),
  890. Name: c.GetName(),
  891. Spec: spec,
  892. StartedAt: c.StartedAt,
  893. LastFinishedAt: c.LastFinishedAt,
  894. RestartCount: c.RestartCount,
  895. }, nil
  896. }
  897. func (c *SContainer) PrepareSaveImage(ctx context.Context, userCred mcclient.TokenCredential, input *api.ContainerSaveVolumeMountToImageInput) (string, error) {
  898. imageInput := &CreateGlanceImageInput{
  899. Name: input.Name,
  900. GenerateName: input.GenerateName,
  901. DiskFormat: imageapi.IMAGE_DISK_FORMAT_TGZ,
  902. Properties: map[string]string{
  903. "notes": input.Notes,
  904. },
  905. // inherit the ownership of disk
  906. ProjectId: c.ProjectId,
  907. }
  908. if len(input.Dirs) > 0 {
  909. dirMap := make(map[string]string, 0)
  910. for _, dir := range input.Dirs {
  911. dirMap[dir] = dir
  912. }
  913. imageInput.Properties[imageapi.IMAGE_INTERNAL_PATH_MAP] = jsonutils.Marshal(dirMap).String()
  914. }
  915. if input.UsedByPostOverlay {
  916. imageInput.Properties[imageapi.IMAGE_USED_BY_POST_OVERLAY] = jsonutils.Marshal(input.UsedByPostOverlay).String()
  917. }
  918. // check class metadata
  919. cm, err := c.GetAllClassMetadata()
  920. if err != nil {
  921. return "", errors.Wrap(err, "unable to GetAllClassMetadata")
  922. }
  923. imageInput.ClassMetadata = cm
  924. return DiskManager.CreateGlanceImage(ctx, userCred, imageInput)
  925. }
  926. func (c *SContainer) PerformSaveVolumeMountImage(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.ContainerSaveVolumeMountToImageInput) (*hostapi.ContainerSaveVolumeMountToImageInput, error) {
  927. if c.GetStatus() != api.CONTAINER_STATUS_EXITED {
  928. return nil, httperrors.NewInvalidStatusError("Can't save volume disk of container in status %s", c.Status)
  929. }
  930. if c.GetPod().GetStatus() != api.VM_READY {
  931. return nil, httperrors.NewInvalidStatusError("Can't save volume disk of pod in status %s", c.GetPod().GetStatus())
  932. }
  933. vols := c.GetVolumeMounts()
  934. if input.Index < 0 || input.Index >= len(vols) {
  935. return nil, httperrors.NewInputParameterError("Only %d volume_mounts", len(vols))
  936. }
  937. imageId, err := c.PrepareSaveImage(ctx, userCred, input)
  938. if err != nil {
  939. return nil, errors.Wrap(err, "prepare to save image")
  940. }
  941. vrs, err := c.GetVolumeMountRelations()
  942. if err != nil {
  943. return nil, errors.Wrap(err, "GetVolumeMountRelations")
  944. }
  945. hvm, err := vrs[input.Index].ToHostMount(ctx, userCred)
  946. if err != nil {
  947. return nil, errors.Wrap(err, "ToHostMount")
  948. }
  949. cleanupDirPath := func(dirPath string) string {
  950. nPath := ""
  951. pathSegs := strings.Split(dirPath, "/")
  952. for _, seg := range pathSegs {
  953. if len(seg) > 0 {
  954. nPath = filepath.Join(nPath, seg)
  955. }
  956. }
  957. return nPath
  958. }
  959. cleanupDirPaths := func(dirPaths []string) []string {
  960. for i := range dirPaths {
  961. dirPaths[i] = cleanupDirPath(dirPaths[i])
  962. }
  963. return dirPaths
  964. }
  965. hostInput := &hostapi.ContainerSaveVolumeMountToImageInput{
  966. ImageId: imageId,
  967. VolumeMountIndex: input.Index,
  968. VolumeMount: hvm,
  969. VolumeMountDirs: cleanupDirPaths(input.Dirs),
  970. VolumeMountPrefix: cleanupDirPath(input.DirPrefix),
  971. ExcludePaths: cleanupDirPaths(input.ExcludePaths),
  972. }
  973. return hostInput, c.StartSaveVolumeMountImage(ctx, userCred, hostInput, "")
  974. }
  975. func (c *SContainer) StartSaveVolumeMountImage(ctx context.Context, userCred mcclient.TokenCredential, input *hostapi.ContainerSaveVolumeMountToImageInput, parentTaskId string) error {
  976. c.SetStatus(ctx, userCred, api.CONTAINER_STATUS_SAVING_IMAGE, "")
  977. task, err := taskman.TaskManager.NewTask(ctx, "ContainerSaveVolumeMountImageTask", c, userCred, jsonutils.Marshal(input).(*jsonutils.JSONDict), parentTaskId, "", nil)
  978. if err != nil {
  979. return errors.Wrap(err, "NewTask")
  980. }
  981. return task.ScheduleRun(nil)
  982. }
  983. func (c *SContainer) GetPodDriver() IPodDriver {
  984. driver, err := c.GetPod().GetDriver()
  985. if err != nil {
  986. return nil
  987. }
  988. return driver.(IPodDriver)
  989. }
  990. func (c *SContainer) GetDetailsExecInfo(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*api.ContainerExecInfoOutput, error) {
  991. gst := c.GetPod()
  992. host, err := gst.GetHost()
  993. if err != nil {
  994. return nil, errors.Wrap(err, "GetHost")
  995. }
  996. out := &api.ContainerExecInfoOutput{
  997. HostUri: host.ManagerUri,
  998. PodId: c.GuestId,
  999. ContainerId: c.Id,
  1000. }
  1001. return out, nil
  1002. }
  1003. func (c *SContainer) PerformExecSync(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.ContainerExecSyncInput) (jsonutils.JSONObject, error) {
  1004. if sets.NewString(
  1005. api.CONTAINER_STATUS_EXITED,
  1006. api.CONTAINER_STATUS_CREATED,
  1007. ).Has(c.GetStatus()) {
  1008. return nil, httperrors.NewInvalidStatusError("Can't exec container in status %s", c.Status)
  1009. }
  1010. return c.GetPodDriver().RequestExecSyncContainer(ctx, userCred, c, input)
  1011. }
  1012. func (c *SContainer) PerformSetResourcesLimit(ctx context.Context, userCred mcclient.TokenCredential, _ jsonutils.JSONObject, input *api.ContainerResourcesSetInput) (jsonutils.JSONObject, error) {
  1013. limit := &input.ContainerResources
  1014. if err := c.ValidateResourcesLimit(limit, input.DisableLimitCheck); err != nil {
  1015. return nil, errors.Wrap(err, "ValidateResourcesLimit")
  1016. }
  1017. if _, err := db.Update(c, func() error {
  1018. c.Spec.ResourcesLimit = limit
  1019. return nil
  1020. }); err != nil {
  1021. return nil, errors.Wrap(err, "Update spec.resources_limit")
  1022. }
  1023. if !api.ContainerRunningStatus.Has(c.GetStatus()) {
  1024. return nil, nil
  1025. }
  1026. return c.GetPodDriver().RequestSetContainerResourcesLimit(ctx, userCred, c, limit)
  1027. }
  1028. func (c *SContainer) ValidateResourcesLimit(limit *apis.ContainerResources, disableLimitCheck bool) error {
  1029. if limit == nil {
  1030. return httperrors.NewInputParameterError("limit cannot be nil")
  1031. }
  1032. pod := c.GetPod()
  1033. if limit.CpuCfsQuota != nil {
  1034. if *limit.CpuCfsQuota <= 0 {
  1035. return httperrors.NewInputParameterError("invalid cpu_cfs_quota %f", *limit.CpuCfsQuota)
  1036. }
  1037. if !disableLimitCheck {
  1038. if *limit.CpuCfsQuota > float64(pod.VcpuCount) {
  1039. return httperrors.NewInputParameterError("cpu_cfs_quota %f can't large than %d", *limit.CpuCfsQuota, pod.VcpuCount)
  1040. }
  1041. }
  1042. }
  1043. return nil
  1044. }
  1045. type ContainerReleasedDevice struct {
  1046. *api.ContainerDevice
  1047. DeviceType string
  1048. DeviceModel string
  1049. }
  1050. func NewContainerReleasedDevice(device *api.ContainerDevice, devType, devModel string) *ContainerReleasedDevice {
  1051. return &ContainerReleasedDevice{
  1052. ContainerDevice: device,
  1053. DeviceType: devType,
  1054. DeviceModel: devModel,
  1055. }
  1056. }
  1057. func (c *SContainer) SaveReleasedDevices(ctx context.Context, userCred mcclient.TokenCredential, devs map[string]ContainerReleasedDevice) error {
  1058. return c.SetMetadata(ctx, api.CONTAINER_METADATA_RELEASED_DEVICES, devs, userCred)
  1059. }
  1060. func (c *SContainer) GetReleasedDevices(ctx context.Context, userCred mcclient.TokenCredential) (map[string]ContainerReleasedDevice, error) {
  1061. out := make(map[string]ContainerReleasedDevice, 0)
  1062. if ret := c.GetMetadata(ctx, api.CONTAINER_METADATA_RELEASED_DEVICES, userCred); ret == "" {
  1063. return out, nil
  1064. }
  1065. obj := c.GetMetadataJson(ctx, api.CONTAINER_METADATA_RELEASED_DEVICES, userCred)
  1066. if obj == nil {
  1067. return nil, errors.Error("get metadata released devices")
  1068. }
  1069. if err := obj.Unmarshal(&out); err != nil {
  1070. return nil, errors.Wrap(err, "Unmarshal metadata released devices")
  1071. }
  1072. return out, nil
  1073. }
  1074. func (c *SContainer) PerformStatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ContainerPerformStatusInput) (jsonutils.JSONObject, error) {
  1075. if api.ContainerExitedStatus.Has(c.GetStatus()) {
  1076. if sets.NewString(api.CONTAINER_STATUS_PROBE_FAILED, api.CONTAINER_STATUS_NET_FAILED).Has(input.Status) {
  1077. return nil, httperrors.NewInputParameterError("can't set container status to %s when %s", input.Status, c.Status)
  1078. }
  1079. }
  1080. if _, err := db.Update(c, func() error {
  1081. if input.RestartCount > 0 {
  1082. c.RestartCount = input.RestartCount
  1083. }
  1084. if api.ContainerRunningStatus.Has(input.Status) {
  1085. // 当容器状态是运行时 restart_count 重新计数
  1086. c.RestartCount = 0
  1087. }
  1088. if input.StartedAt != nil {
  1089. c.StartedAt = *input.StartedAt
  1090. }
  1091. if input.LastFinishedAt != nil {
  1092. c.LastFinishedAt = *input.LastFinishedAt
  1093. }
  1094. return nil
  1095. }); err != nil {
  1096. return nil, errors.Wrap(err, "Update container status")
  1097. }
  1098. return c.SVirtualResourceBase.PerformStatus(ctx, userCred, query, input.PerformStatusInput)
  1099. }
  1100. func (c *SContainer) getContainerHostCommitInput(ctx context.Context, userCred mcclient.TokenCredential, input *api.ContainerCommitInput) (*hostapi.ContainerCommitInput, error) {
  1101. var hostInput = &hostapi.ContainerCommitInput{
  1102. Auth: new(apis.ContainerPullImageAuthConfig),
  1103. }
  1104. var repoUrl string
  1105. imageName := input.ImageName
  1106. tag := input.Tag
  1107. if imageName == "" {
  1108. imageName = c.GetName()
  1109. }
  1110. if tag == "" {
  1111. tag = time.Now().Format("20060102150405")
  1112. }
  1113. if input.RegistryId != "" {
  1114. s := auth.GetSession(ctx, userCred, consts.GetRegion())
  1115. obj, err := kubemod.ContainerRegistries.Get(s, input.RegistryId, nil)
  1116. if err != nil {
  1117. return nil, httperrors.NewGeneralError(err)
  1118. }
  1119. reg := new(api.KubeServerContainerRegistryDetails)
  1120. if err := obj.Unmarshal(reg); err != nil {
  1121. return nil, errors.Wrap(err, "Unmarshal kube server registry details")
  1122. }
  1123. if reg.Config == nil {
  1124. confObj, err := kubemod.ContainerRegistries.GetSpecific(s, input.RegistryId, "config", nil)
  1125. if err != nil {
  1126. return nil, errors.Wrap(err, "Get kube server registry config")
  1127. }
  1128. reg.Config = new(api.KubeServerContainerRegistryConfig)
  1129. if err := confObj.Unmarshal(reg.Config); err != nil {
  1130. return nil, errors.Wrap(err, "Unmarshal kube server registry config")
  1131. }
  1132. }
  1133. repoUrl = reg.Url
  1134. if reg.Config != nil {
  1135. switch reg.Type {
  1136. case "common":
  1137. cfg := reg.Config.Common
  1138. hostInput.Auth.Username = cfg.Username
  1139. hostInput.Auth.Password = cfg.Password
  1140. case "harbor":
  1141. cfg := reg.Config.Harbor
  1142. hostInput.Auth.Username = cfg.Username
  1143. hostInput.Auth.Password = cfg.Password
  1144. default:
  1145. return nil, httperrors.NewInputParameterError("invalid registry type %s", reg.Type)
  1146. }
  1147. }
  1148. } else if input.ExternalRegistry != nil {
  1149. repoUrl = input.ExternalRegistry.Url
  1150. if repoUrl == "" {
  1151. return nil, httperrors.NewNotEmptyError("empty external registry url")
  1152. }
  1153. hostInput.Auth = input.ExternalRegistry.Auth
  1154. } else {
  1155. return nil, httperrors.NewInputParameterError("one of registry_id or external_registry must provided")
  1156. }
  1157. repoPrefix := strings.TrimPrefix(strings.TrimPrefix(repoUrl, "http://"), "https://")
  1158. hostInput.Repository = fmt.Sprintf("%s:%s", filepath.Join(repoPrefix, imageName), tag)
  1159. return hostInput, nil
  1160. }
  1161. func (c *SContainer) PerformCommit(ctx context.Context, userCred mcclient.TokenCredential, _ jsonutils.JSONObject, input *api.ContainerCommitInput) (*api.ContainerCommitOutput, error) {
  1162. hostInput, err := c.getContainerHostCommitInput(ctx, userCred, input)
  1163. if err != nil {
  1164. return nil, err
  1165. }
  1166. out := &api.ContainerCommitOutput{
  1167. Repository: hostInput.Repository,
  1168. }
  1169. return out, c.StartCommit(ctx, userCred, hostInput, "")
  1170. }
  1171. func (c *SContainer) StartCommit(ctx context.Context, userCred mcclient.TokenCredential, input *hostapi.ContainerCommitInput, parentTaskId string) error {
  1172. c.SetStatus(ctx, userCred, api.CONTAINER_STATUS_COMMITTING, "")
  1173. task, err := taskman.TaskManager.NewTask(ctx, "ContainerCommitTask", c, userCred, jsonutils.Marshal(input).(*jsonutils.JSONDict), parentTaskId, "", nil)
  1174. if err != nil {
  1175. return errors.Wrap(err, "NewTask")
  1176. }
  1177. return task.ScheduleRun(nil)
  1178. }
  1179. func (c *SContainer) isPostOverlayExist(vm *apis.ContainerVolumeMount, ov *apis.ContainerVolumeMountDiskPostOverlay) (*apis.ContainerVolumeMountDiskPostOverlay, bool) {
  1180. for i := range vm.Disk.PostOverlay {
  1181. cov := vm.Disk.PostOverlay[i]
  1182. if cov.IsEqual(*ov) {
  1183. return cov, true
  1184. }
  1185. }
  1186. return nil, false
  1187. }
  1188. func (c *SContainer) validateVolumeMountPostOverlayAction(action string, index int) (*apis.ContainerVolumeMount, error) {
  1189. if !api.ContainerExitedStatus.Has(c.Status) && !api.ContainerRunningStatus.Has(c.Status) {
  1190. return nil, httperrors.NewInvalidStatusError("can't %s post overlay on status %s", action, c.Status)
  1191. }
  1192. if index >= len(c.Spec.VolumeMounts) {
  1193. return nil, httperrors.NewInputParameterError("index %d out of volume_mount size %d", index, len(c.Spec.VolumeMounts))
  1194. }
  1195. vm := new(apis.ContainerVolumeMount)
  1196. curVm := c.Spec.VolumeMounts[index]
  1197. if err := jsonutils.Marshal(curVm).Unmarshal(vm); err != nil {
  1198. return nil, errors.Wrap(err, "use json unmarshal to new volume mount")
  1199. }
  1200. if vm.Type != apis.CONTAINER_VOLUME_MOUNT_TYPE_DISK {
  1201. return nil, httperrors.NewInputParameterError("invalid volume mount type %s", vm.Type)
  1202. }
  1203. return vm, nil
  1204. }
  1205. func (c *SContainer) GetVolumeMountCopy(index int) (*apis.ContainerVolumeMount, error) {
  1206. if index >= len(c.Spec.VolumeMounts) {
  1207. return nil, httperrors.NewInputParameterError("index %d out of volume_mount size %d", index, len(c.Spec.VolumeMounts))
  1208. }
  1209. vm := new(apis.ContainerVolumeMount)
  1210. curVm := c.Spec.VolumeMounts[index]
  1211. if err := jsonutils.Marshal(curVm).Unmarshal(vm); err != nil {
  1212. return nil, errors.Wrap(err, "use json unmarshal to new volume mount")
  1213. }
  1214. return vm, nil
  1215. }
  1216. func (c *SContainer) getPostOverlayVolumeMount(
  1217. index int,
  1218. updateF func(mount *apis.ContainerVolumeMount) (*apis.ContainerVolumeMount, error),
  1219. ) (*apis.ContainerVolumeMount, error) {
  1220. vm, err := c.GetVolumeMountCopy(index)
  1221. if err != nil {
  1222. return nil, err
  1223. }
  1224. return updateF(vm)
  1225. }
  1226. func (c *SContainer) GetAddPostOverlayVolumeMount(index int, ovs []*apis.ContainerVolumeMountDiskPostOverlay) (*apis.ContainerVolumeMount, error) {
  1227. return c.getPostOverlayVolumeMount(index, func(vm *apis.ContainerVolumeMount) (*apis.ContainerVolumeMount, error) {
  1228. if vm.Disk.PostOverlay == nil {
  1229. vm.Disk.PostOverlay = []*apis.ContainerVolumeMountDiskPostOverlay{}
  1230. }
  1231. vm.Disk.PostOverlay = append(vm.Disk.PostOverlay, ovs...)
  1232. return vm, nil
  1233. })
  1234. }
  1235. func (c *SContainer) GetRemovePostOverlayVolumeMount(index int, ovs []*apis.ContainerVolumeMountDiskPostOverlay) (*apis.ContainerVolumeMount, error) {
  1236. return c.getPostOverlayVolumeMount(index, func(vm *apis.ContainerVolumeMount) (*apis.ContainerVolumeMount, error) {
  1237. // remove post overlay
  1238. for _, ov := range ovs {
  1239. vm.Disk = c.removePostOverlay(vm.Disk, ov)
  1240. }
  1241. return vm, nil
  1242. })
  1243. }
  1244. func (c *SContainer) PerformAddVolumeMountPostOverlay(ctx context.Context, userCred mcclient.TokenCredential, _ jsonutils.JSONObject, input *api.ContainerVolumeMountAddPostOverlayInput) (jsonutils.JSONObject, error) {
  1245. vm, err := c.validateVolumeMountPostOverlayAction("add", input.Index)
  1246. if err != nil {
  1247. return nil, err
  1248. }
  1249. totalOvs := []*apis.ContainerVolumeMountDiskPostOverlay{}
  1250. totalOvs = append(totalOvs, vm.Disk.PostOverlay...)
  1251. drv := GetContainerVolumeMountDriver(vm.Type)
  1252. dDrv := drv.(IContainerVolumeMountDiskDriver)
  1253. for i := range input.PostOverlay {
  1254. ov := input.PostOverlay[i]
  1255. cov, isExist := c.isPostOverlayExist(vm, ov)
  1256. if isExist {
  1257. return nil, httperrors.NewInputParameterError("post overlay already exists: %s", jsonutils.Marshal(cov))
  1258. }
  1259. if err := dDrv.ValidatePostSingleOverlay(ctx, userCred, ov); err != nil {
  1260. return nil, errors.Wrapf(err, "validate post overlay %s", jsonutils.Marshal(ov))
  1261. }
  1262. totalOvs = append(totalOvs, ov)
  1263. }
  1264. if err := dDrv.ValidatePostOverlayTargetDirs(totalOvs); err != nil {
  1265. return nil, errors.Wrapf(err, "validate container target dirs")
  1266. }
  1267. return nil, c.StartAddVolumeMountPostOverlayTask(ctx, userCred, input, "")
  1268. }
  1269. func (c *SContainer) StartCacheImagesTask(ctx context.Context, userCred mcclient.TokenCredential, input *api.ContainerCacheImagesInput, parentTaskId string) error {
  1270. c.SetStatus(ctx, userCred, api.CONTAINER_STATUS_CACHE_IMAGE, "")
  1271. task, err := taskman.TaskManager.NewTask(ctx, "ContainerCacheImagesTask", c, userCred, jsonutils.Marshal(input).(*jsonutils.JSONDict), parentTaskId, "", nil)
  1272. if err != nil {
  1273. return errors.Wrap(err, "New ContainerCacheImagesTask")
  1274. }
  1275. return task.ScheduleRun(nil)
  1276. }
  1277. func (c *SContainer) StartAddVolumeMountPostOverlayTask(ctx context.Context, userCred mcclient.TokenCredential, input *api.ContainerVolumeMountAddPostOverlayInput, parentTaskId string) error {
  1278. c.SetStatus(ctx, userCred, api.CONTAINER_STATUS_ADD_POST_OVERLY, "")
  1279. task, err := taskman.TaskManager.NewTask(ctx, "ContainerAddVolumeMountPostOverlayTask", c, userCred, jsonutils.Marshal(input).(*jsonutils.JSONDict), parentTaskId, "", nil)
  1280. if err != nil {
  1281. return errors.Wrap(err, "New ContainerAddVolumeMountPostOverlayTask")
  1282. }
  1283. return task.ScheduleRun(nil)
  1284. }
  1285. func (c *SContainer) StartRemoveVolumeMountPostOverlayTask(ctx context.Context, userCred mcclient.TokenCredential, input *api.ContainerVolumeMountRemovePostOverlayInput, parentTaskId string) error {
  1286. c.SetStatus(ctx, userCred, api.CONTAINER_STATUS_REMOVE_POST_OVERLY, "")
  1287. task, err := taskman.TaskManager.NewTask(ctx, "ContainerRemoveVolumeMountPostOverlayTask", c, userCred, jsonutils.Marshal(input).(*jsonutils.JSONDict), parentTaskId, "", nil)
  1288. if err != nil {
  1289. return errors.Wrap(err, "New ContainerRemoveVolumeMountPostOverlayTask")
  1290. }
  1291. return task.ScheduleRun(nil)
  1292. }
  1293. func (c *SContainer) PerformRemoveVolumeMountPostOverlay(ctx context.Context, userCred mcclient.TokenCredential, _ jsonutils.JSONObject, input *api.ContainerVolumeMountRemovePostOverlayInput) (jsonutils.JSONObject, error) {
  1294. vm, err := c.validateVolumeMountPostOverlayAction("remove", input.Index)
  1295. if err != nil {
  1296. return nil, err
  1297. }
  1298. if len(vm.Disk.PostOverlay) == 0 {
  1299. return nil, httperrors.NewInputParameterError("no post overlay")
  1300. }
  1301. for i, ov := range input.PostOverlay {
  1302. cov, isExist := c.isPostOverlayExist(vm, ov)
  1303. if !isExist {
  1304. return nil, httperrors.NewInputParameterError("post overlay not exists: %s", jsonutils.Marshal(ov))
  1305. }
  1306. input.PostOverlay[i] = cov
  1307. }
  1308. return nil, c.StartRemoveVolumeMountPostOverlayTask(ctx, userCred, input, "")
  1309. }
  1310. func (c *SContainer) removePostOverlay(vmd *apis.ContainerVolumeMountDisk, ov *apis.ContainerVolumeMountDiskPostOverlay) *apis.ContainerVolumeMountDisk {
  1311. curOvs := vmd.PostOverlay
  1312. resultOvs := []*apis.ContainerVolumeMountDiskPostOverlay{}
  1313. for i := range curOvs {
  1314. cov := curOvs[i]
  1315. if cov.IsEqual(*ov) {
  1316. continue
  1317. }
  1318. resultOvs = append(resultOvs, cov)
  1319. }
  1320. vmd.PostOverlay = resultOvs
  1321. return vmd
  1322. }
  1323. func (c *SContainer) SetStatusFromHost(ctx context.Context, userCred mcclient.TokenCredential, resp api.ContainerSyncStatusResponse, reason string) error {
  1324. errs := []error{}
  1325. if err := c.SetStatus(ctx, userCred, resp.Status, reason); err != nil {
  1326. errs = append(errs, errors.Wrap(err, "SetStatus"))
  1327. }
  1328. if _, err := db.Update(c, func() error {
  1329. if resp.RestartCount > 0 {
  1330. c.RestartCount = resp.RestartCount
  1331. }
  1332. c.StartedAt = resp.StartedAt
  1333. return nil
  1334. }); err != nil {
  1335. errs = append(errs, errors.Wrap(err, "Update container started_at: %s"))
  1336. }
  1337. return errors.NewAggregate(errs)
  1338. }