| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464 |
- // Copyright 2019 Yunion
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package models
- import (
- "context"
- "encoding/base64"
- "fmt"
- "path/filepath"
- "strings"
- "time"
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/pkg/gotypes"
- "yunion.io/x/pkg/util/sets"
- "yunion.io/x/sqlchemy"
- "yunion.io/x/onecloud/pkg/apis"
- api "yunion.io/x/onecloud/pkg/apis/compute"
- hostapi "yunion.io/x/onecloud/pkg/apis/host"
- identityapi "yunion.io/x/onecloud/pkg/apis/identity"
- imageapi "yunion.io/x/onecloud/pkg/apis/image"
- "yunion.io/x/onecloud/pkg/cloudcommon/consts"
- "yunion.io/x/onecloud/pkg/cloudcommon/db"
- "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
- "yunion.io/x/onecloud/pkg/compute/options"
- "yunion.io/x/onecloud/pkg/compute/utils"
- "yunion.io/x/onecloud/pkg/httperrors"
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/mcclient/auth"
- identitymod "yunion.io/x/onecloud/pkg/mcclient/modules/identity"
- kubemod "yunion.io/x/onecloud/pkg/mcclient/modules/k8s"
- )
- var containerManager *SContainerManager
- func GetContainerManager() *SContainerManager {
- if containerManager == nil {
- containerManager = &SContainerManager{
- SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager(
- SContainer{},
- "containers_tbl",
- "container",
- "containers"),
- }
- containerManager.SetVirtualObject(containerManager)
- }
- return containerManager
- }
- func init() {
- GetContainerManager()
- }
- type SContainerManager struct {
- db.SVirtualResourceBaseManager
- }
- type SContainer struct {
- db.SVirtualResourceBase
- db.SExternalizedResourceBase
- // GuestId is also the pod id
- GuestId string `width:"36" charset:"ascii" create:"required" list:"user" index:"true"`
- // Spec stores all container running options
- Spec *api.ContainerSpec `length:"long" create:"required" list:"user" update:"user"`
- // 启动时间
- StartedAt time.Time `nullable:"true" created_at:"false" index:"true" get:"user" list:"user" json:"started_at"`
- // 上次退出时间
- LastFinishedAt time.Time `nullable:"true" created_at:"false" index:"true" get:"user" list:"user" json:"last_finished_at"`
- // 重启次数
- RestartCount int `nullable:"true" list:"user"`
- }
- func (m *SContainerManager) CreateOnPod(
- ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider,
- pod *SGuest, data *api.PodContainerCreateInput) (*SContainer, error) {
- input := &api.ContainerCreateInput{
- GuestId: pod.GetId(),
- Spec: data.ContainerSpec,
- SkipTask: true,
- }
- input.Name = data.Name
- obj, err := db.DoCreate(m, ctx, userCred, nil, jsonutils.Marshal(input), ownerId)
- if err != nil {
- return nil, errors.Wrap(err, "create container")
- }
- return obj.(*SContainer), nil
- }
- func (m *SContainerManager) FetchUniqValues(ctx context.Context, data jsonutils.JSONObject) jsonutils.JSONObject {
- guestId, _ := data.GetString("guest_id")
- return jsonutils.Marshal(map[string]string{"guest_id": guestId})
- }
- func (m *SContainerManager) FilterByUniqValues(q *sqlchemy.SQuery, values jsonutils.JSONObject) *sqlchemy.SQuery {
- guestId, _ := values.GetString("guest_id")
- if len(guestId) > 0 {
- q = q.Equals("guest_id", guestId)
- }
- return q
- }
- func (m *SContainerManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query api.ContainerListInput) (*sqlchemy.SQuery, error) {
- q, err := m.SVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, query.VirtualResourceListInput)
- if err != nil {
- return nil, errors.Wrap(err, "SVirtualResourceBaseManager.ListItemFilter")
- }
- if query.GuestId != "" {
- gst, err := GuestManager.FetchByIdOrName(ctx, userCred, query.GuestId)
- if err != nil {
- return nil, errors.Wrapf(err, "fetch guest by %s", query.GuestId)
- }
- q = q.Equals("guest_id", gst.GetId())
- }
- if query.HostId != "" {
- host, _ := HostManager.FetchByIdOrName(ctx, nil, query.HostId)
- if host == nil {
- return nil, httperrors.NewResourceNotFoundError("host %s not found", query.HostId)
- }
- gst := GuestManager.Query().SubQuery()
- q = q.Join(gst, sqlchemy.Equals(q.Field("guest_id"), gst.Field("id")))
- q = q.Filter(sqlchemy.Equals(gst.Field("host_id"), host.GetId()))
- }
- return q, nil
- }
- func (m *SContainerManager) GetContainersByPod(guestId string) ([]SContainer, error) {
- q := m.Query().Equals("guest_id", guestId).Asc("created_at")
- ctrs := make([]SContainer, 0)
- if err := db.FetchModelObjects(m, q, &ctrs); err != nil {
- return nil, errors.Wrap(err, "db.FetchModelObjects")
- }
- return ctrs, nil
- }
- func (m *SContainerManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, _ jsonutils.JSONObject, input *api.ContainerCreateInput) (*api.ContainerCreateInput, error) {
- if input.GuestId == "" {
- return nil, httperrors.NewNotEmptyError("guest_id is required")
- }
- obj, err := GuestManager.FetchByIdOrName(ctx, userCred, input.GuestId)
- if err != nil {
- return nil, errors.Wrapf(err, "fetch guest by %s", input.GuestId)
- }
- pod := obj.(*SGuest)
- input.GuestId = pod.GetId()
- if err := m.ValidateSpec(ctx, userCred, &input.Spec, pod, nil); err != nil {
- return nil, errors.Wrap(err, "validate spec")
- }
- return input, nil
- }
- func (m *SContainerManager) ValidateSpec(ctx context.Context, userCred mcclient.TokenCredential, spec *api.ContainerSpec, pod *SGuest, ctr *SContainer) error {
- if spec.ImagePullPolicy == "" {
- spec.ImagePullPolicy = apis.ImagePullPolicyIfNotPresent
- }
- if !sets.NewString(apis.ImagePullPolicyAlways, apis.ImagePullPolicyIfNotPresent).Has(string(spec.ImagePullPolicy)) {
- return httperrors.NewInputParameterError("invalid image_pull_policy %s", spec.ImagePullPolicy)
- }
- if spec.ImageCredentialId != "" {
- if _, err := m.GetImageCredential(ctx, userCred, spec.ImageCredentialId); err != nil {
- return errors.Wrapf(err, "get image credential by id: %s", spec.ImageCredentialId)
- }
- }
- if err := m.ValidateSpecEnvs(ctx, userCred, spec); err != nil {
- return errors.Wrap(err, "validate envs")
- }
- if pod != nil {
- if err := m.ValidateSpecRootFs(ctx, userCred, pod, spec, ctr); err != nil {
- return errors.Wrap(err, "ValidateSpecRootFs")
- }
- if err := m.ValidateSpecVolumeMounts(ctx, userCred, pod, spec, ctr); err != nil {
- return errors.Wrap(err, "ValidateSpecVolumeMounts")
- }
- for idx, dev := range spec.Devices {
- newDev, err := m.ValidateSpecDevice(ctx, userCred, pod, dev)
- if err != nil {
- return errors.Wrapf(err, "validate device %s", jsonutils.Marshal(dev))
- }
- spec.Devices[idx] = newDev
- }
- }
- if err := m.ValidateSpecLifecycle(ctx, userCred, spec); err != nil {
- return errors.Wrap(err, "validate lifecycle")
- }
- if spec.ShmSizeMB != 0 && spec.ShmSizeMB < 64 {
- return httperrors.NewInputParameterError("/dev/shm size is small than 64MB")
- }
- if err := m.ValidateSpecProbe(ctx, userCred, spec); err != nil {
- return errors.Wrap(err, "validate probe configuration")
- }
- if ctr != nil {
- // only detect loop when update container
- ctrs, err := m.GetContainersByPod(pod.GetId())
- if err != nil {
- return errors.Wrap(err, "get containers by pod")
- }
- for idx, container := range ctrs {
- if container.GetId() == ctr.GetId() {
- ctrs[idx].Spec = spec
- }
- }
- err = utils.TopologicalSortContainers(
- ctrs,
- func(ctr SContainer) string { return ctr.Name },
- func(ctr SContainer) []string { return ctr.Spec.DependsOn },
- )
- if err != nil {
- return errors.Wrap(err, "validate topological sort")
- }
- }
- return nil
- }
- func (m *SContainerManager) ValidateSpecEnvs(ctx context.Context, userCred mcclient.TokenCredential, spec *api.ContainerSpec) error {
- var errs []error
- for _, env := range spec.Envs {
- if env.ValueFrom == nil {
- continue
- }
- if env.ValueFrom.Credential != nil {
- credId := env.ValueFrom.Credential.Id
- if credId == "" {
- errs = append(errs, errors.Wrapf(errors.ErrEmpty, "credential id is empty"))
- continue
- }
- credKey := env.ValueFrom.Credential.Key
- if credKey == "" {
- errs = append(errs, errors.Wrapf(errors.ErrEmpty, "credential key is empty"))
- continue
- }
- cred, err := m.GetSecretCredential(ctx, userCred, credId)
- if err != nil {
- errs = append(errs, errors.Wrapf(err, "get secret credential %s", credId))
- }
- _, ok := cred[credKey]
- if !ok {
- errs = append(errs, errors.Wrapf(errors.ErrNotFound, "env %s secret credential %s key %s not found", env.Key, credId, credKey))
- }
- }
- }
- return errors.NewAggregate(errs)
- }
- func (m *SContainerManager) ValidateSpecLifecycle(ctx context.Context, cred mcclient.TokenCredential, spec *api.ContainerSpec) error {
- if spec.Lifecyle == nil {
- return nil
- }
- if err := m.ValidateSpecLifecyclePostStart(ctx, cred, spec.Lifecyle.PostStart); err != nil {
- return errors.Wrap(err, "validate post start")
- }
- return nil
- }
- func (m *SContainerManager) ValidateSpecLifecyclePostStart(ctx context.Context, userCred mcclient.TokenCredential, input *apis.ContainerLifecyleHandler) error {
- drv, err := GetContainerLifecyleDriverWithError(input.Type)
- if err != nil {
- return httperrors.NewInputParameterError("get lifecycle driver: %v", err)
- }
- return drv.ValidateCreateData(ctx, userCred, input)
- }
- func (m *SContainerManager) ValidateSpecDevice(ctx context.Context, userCred mcclient.TokenCredential, pod *SGuest, dev *api.ContainerDevice) (*api.ContainerDevice, error) {
- drv, err := GetContainerDeviceDriverWithError(dev.Type)
- if err != nil {
- return nil, httperrors.NewInputParameterError("get device driver: %v", err)
- }
- return drv.ValidateCreateData(ctx, userCred, pod, dev)
- }
- func (m *SContainerManager) ValidateSpecRootFs(ctx context.Context, userCred mcclient.TokenCredential, pod *SGuest, spec *api.ContainerSpec, ctr *SContainer) error {
- if spec.RootFs == nil {
- return nil
- }
- rootFs := spec.RootFs
- if rootFs.Disk != nil {
- if rootFs.Disk.SubDirectory == "" {
- rootFs.Disk.SubDirectory = "rootfs"
- }
- }
- drv, err := GetContainerRootFsDriverWithError(rootFs.Type)
- if err != nil {
- return errors.Wrapf(err, "get container volume mount driver %q", rootFs.Type)
- }
- if err := drv.ValidateRootFsCreateData(ctx, userCred, pod, rootFs); err != nil {
- return errors.Wrapf(err, "validate %s create data", drv.GetType())
- }
- return nil
- }
- func (m *SContainerManager) ValidateSpecVolumeMounts(ctx context.Context, userCred mcclient.TokenCredential, pod *SGuest, spec *api.ContainerSpec, ctr *SContainer) error {
- relation, err := m.GetVolumeMountRelations(pod, spec)
- if err != nil {
- return errors.Wrap(err, "GetVolumeMountRelations")
- }
- curCtrs, _ := m.GetContainersByPod(pod.GetId())
- volUniqNames := sets.NewString()
- for idx := range curCtrs {
- if ctr != nil && ctr.GetId() == curCtrs[idx].GetId() {
- continue
- }
- for _, vol := range curCtrs[idx].Spec.VolumeMounts {
- if vol.UniqueName != "" {
- volUniqNames.Insert(vol.UniqueName)
- }
- }
- }
- for idx, vm := range spec.VolumeMounts {
- if vm.UniqueName != "" {
- if volUniqNames.Has(vm.UniqueName) {
- return httperrors.NewDuplicateNameError("volume_mount unique_name %s", fmt.Sprintf("%s: %s, index: %d", vm.UniqueName, jsonutils.Marshal(vm), idx))
- } else {
- volUniqNames.Insert(vm.UniqueName)
- }
- }
- newVm, err := m.ValidateSpecVolumeMount(ctx, userCred, pod, vm)
- if err != nil {
- return errors.Wrapf(err, "validate volume mount %s", jsonutils.Marshal(vm))
- }
- spec.VolumeMounts[idx] = newVm
- }
- if _, err := m.ConvertVolumeMountRelationToSpec(ctx, userCred, relation); err != nil {
- return errors.Wrap(err, "ConvertVolumeMountRelationToSpec")
- }
- return nil
- }
- func (m *SContainerManager) ValidateSpecVolumeMount(ctx context.Context, userCred mcclient.TokenCredential, pod *SGuest, vm *apis.ContainerVolumeMount) (*apis.ContainerVolumeMount, error) {
- if vm.Type == "" {
- return nil, httperrors.NewNotEmptyError("type is required")
- }
- if vm.MountPath == "" {
- return nil, httperrors.NewNotEmptyError("mount_path is required")
- }
- drv, err := GetContainerVolumeMountDriverWithError(vm.Type)
- if err != nil {
- return nil, errors.Wrapf(err, "get container volume mount driver %s", vm.Type)
- }
- vm, err = drv.ValidateCreateData(ctx, userCred, pod, vm)
- if err != nil {
- return nil, errors.Wrapf(err, "validate %s create data", drv.GetType())
- }
- return vm, nil
- }
- /*func (m *SContainerManager) GetContainerIndex(guestId string) (int, error) {
- cnt, err := m.Query("guest_id").Equals("guest_id", guestId).CountWithError()
- if err != nil {
- return -1, errors.Wrapf(err, "get container numbers of pod %s", guestId)
- }
- return cnt, nil
- }
- func (c *SContainer) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
- input := new(api.ContainerCreateInput)
- if err := data.Unmarshal(input); err != nil {
- return errors.Wrap(err, "unmarshal to ContainerCreateInput")
- }
- if input.Spec.ImagePullPolicy == "" {
- c.Spec.ImagePullPolicy = apis.ImagePullPolicyIfNotPresent
- }
- return nil
- }*/
- func (m *SContainerManager) ValidateSpecProbe(ctx context.Context, userCred mcclient.TokenCredential, spec *api.ContainerSpec) error {
- //if err := m.validateSpecProbe(ctx, userCred, spec.LivenessProbe); err != nil {
- // return errors.Wrap(err, "validate liveness probe")
- //}
- if err := m.validateSpecProbe(ctx, userCred, spec.StartupProbe); err != nil {
- return errors.Wrap(err, "validate startup probe")
- }
- return nil
- }
- func (m *SContainerManager) validateSpecProbe(ctx context.Context, userCred mcclient.TokenCredential, probe *apis.ContainerProbe) error {
- if probe == nil {
- return nil
- }
- if err := m.validateSpecProbeHandler(probe.ContainerProbeHandler); err != nil {
- return errors.Wrap(err, "validate container probe handler")
- }
- for key, val := range map[string]int32{
- //"initial_delay_seconds": probe.InitialDelaySeconds,
- "timeout_seconds": probe.TimeoutSeconds,
- "period_seconds": probe.PeriodSeconds,
- "success_threshold": probe.SuccessThreshold,
- "failure_threshold": probe.FailureThreshold,
- } {
- if val < 0 {
- return httperrors.NewInputParameterError("%s is negative", key)
- }
- }
- //if probe.InitialDelaySeconds == 0 {
- // probe.InitialDelaySeconds = 5
- //}
- if probe.TimeoutSeconds == 0 {
- probe.TimeoutSeconds = 3
- }
- if probe.PeriodSeconds == 0 {
- probe.PeriodSeconds = 10
- }
- if probe.SuccessThreshold == 0 {
- probe.SuccessThreshold = 1
- }
- if probe.FailureThreshold == 0 {
- probe.FailureThreshold = 3
- }
- return nil
- }
- func (m *SContainerManager) validateSpecProbeHandler(probe apis.ContainerProbeHandler) error {
- isAllNil := true
- if probe.Exec != nil {
- isAllNil = false
- if len(probe.Exec.Command) == 0 {
- return httperrors.NewInputParameterError("exec command is required")
- }
- }
- if probe.TCPSocket != nil {
- isAllNil = false
- port := probe.TCPSocket.Port
- if port < 1 || port > 65535 {
- return httperrors.NewInputParameterError("invalid tcp socket port: %d, must between [1,65535]", port)
- }
- }
- if probe.HTTPGet != nil {
- isAllNil = false
- port := probe.HTTPGet.Port
- if port < 1 || port > 65535 {
- return httperrors.NewInputParameterError("invalid http port: %d, must between [1,65535]", port)
- }
- }
- if isAllNil {
- return httperrors.NewInputParameterError("one of [exec, http_get, tcp_socket] is required")
- }
- return nil
- }
- func (m *SContainerManager) startBatchTask(ctx context.Context, userCred mcclient.TokenCredential, taskName string, ctrs []SContainer, taskData *jsonutils.JSONDict, parentTaskId string) error {
- ctrPtrs := make([]db.IStandaloneModel, len(ctrs))
- for i := range ctrs {
- ctrPtrs[i] = &ctrs[i]
- }
- task, err := taskman.TaskManager.NewParallelTask(ctx, taskName, ctrPtrs, userCred, taskData, parentTaskId, "")
- if err != nil {
- return errors.Wrapf(err, "NewParallelTask %s", taskName)
- }
- return task.ScheduleRun(nil)
- }
- func (m *SContainerManager) StartBatchStartTask(ctx context.Context, userCred mcclient.TokenCredential, ctrs []SContainer, parentTaskId string) error {
- return m.startBatchTask(ctx, userCred, "ContainerBatchStartTask", ctrs, nil, parentTaskId)
- }
- func (m *SContainerManager) StartBatchStopTask(ctx context.Context, userCred mcclient.TokenCredential, ctrs []SContainer, timeout int, force bool, parentTaskId string) error {
- params := make([]api.ContainerStopInput, len(ctrs))
- for i := range ctrs {
- params[i] = api.ContainerStopInput{
- Timeout: timeout,
- Force: force,
- }
- }
- taskParams := jsonutils.NewDict()
- taskParams.Add(jsonutils.Marshal(params), "params")
- return m.startBatchTask(ctx, userCred, "ContainerBatchStopTask", ctrs, taskParams, parentTaskId)
- }
- func (m *SContainerManager) StartBatchRestartTask(ctx context.Context, userCred mcclient.TokenCredential, ctrs []SContainer, timeout int, force bool, parentTaskId string) error {
- params := make([]api.ContainerRestartInput, len(ctrs))
- for i := range ctrs {
- params[i] = api.ContainerRestartInput{
- Timeout: timeout,
- Force: force,
- }
- }
- taskParams := jsonutils.NewDict()
- taskParams.Add(jsonutils.Marshal(params), "params")
- return m.startBatchTask(ctx, userCred, "ContainerBatchRestartTask", ctrs, taskParams, parentTaskId)
- }
- func (c *SContainer) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
- c.SVirtualResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
- if !jsonutils.QueryBoolean(data, "skip_task", false) {
- if err := c.StartCreateTask(ctx, userCred, "", data.(*jsonutils.JSONDict)); err != nil {
- log.Errorf("StartCreateTask error: %v", err)
- }
- }
- }
- func (c *SContainer) StartCreateTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string, params *jsonutils.JSONDict) error {
- task, err := taskman.TaskManager.NewTask(ctx, "ContainerCreateTask", c, userCred, params, parentTaskId, "", nil)
- if err != nil {
- return errors.Wrap(err, "NewTask")
- }
- return task.ScheduleRun(nil)
- }
- // func (c *SContainer) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.ContainerUpdateInput) (*api.ContainerUpdateInput, error) {
- func (c *SContainer) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (*api.ContainerUpdateInput, error) {
- if !api.ContainerExitedStatus.Has(c.GetStatus()) {
- return nil, httperrors.NewInvalidStatusError("current status %s is not in %v", c.GetStatus(), api.ContainerExitedStatus.List())
- }
- input := new(api.ContainerUpdateInput)
- if err := data.Unmarshal(input); err != nil {
- return nil, errors.Wrap(err, "Unmarshal")
- }
- baseInput, err := c.SVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input.VirtualResourceBaseUpdateInput)
- if err != nil {
- return input, errors.Wrap(err, "SVirtualResourceBase.ValidateUpdateData")
- }
- input.VirtualResourceBaseUpdateInput = baseInput
- if err := GetContainerManager().ValidateSpec(ctx, userCred, &input.Spec, c.GetPod(), c); err != nil {
- return nil, errors.Wrap(err, "validate spec")
- }
- return input, nil
- }
- func (c *SContainer) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) {
- c.SVirtualResourceBase.PostUpdate(ctx, userCred, query, data)
- if err := c.GetPod().StartSyncTaskWithoutSyncstatus(ctx, userCred, false, ""); err != nil {
- log.Errorf("container %s StartSyncTaskWithoutSyncstatus error: %v", c.GetName(), err)
- }
- }
- func (c *SContainer) GetPod() *SGuest {
- return GuestManager.FetchGuestById(c.GuestId)
- }
- func (c *SContainer) GetVolumeMounts() []*apis.ContainerVolumeMount {
- return c.Spec.VolumeMounts
- }
- type ContainerRootFsRelation struct {
- RootFs *apis.ContainerRootfs
- pod *SGuest
- }
- func (rr *ContainerRootFsRelation) ToHostRootFs() (*hostapi.ContainerRootfs, error) {
- drv, err := GetContainerRootFsDriverWithError(rr.RootFs.Type)
- if err != nil {
- return nil, errors.Wrapf(err, "get container root fs driver %q", rr.RootFs.Type)
- }
- rootFs, err := drv.ToHostRootFs(rr.RootFs)
- if err != nil {
- return nil, errors.Wrapf(err, "to host root fs")
- }
- return rootFs, nil
- }
- type ContainerVolumeMountRelation struct {
- VolumeMount *apis.ContainerVolumeMount
- pod *SGuest
- }
- func (vm *ContainerVolumeMountRelation) toHostDiskMount(disk *apis.ContainerVolumeMountDisk) (*hostapi.ContainerVolumeMountDisk, error) {
- diskObj := DiskManager.FetchDiskById(disk.Id)
- if diskObj == nil {
- return nil, errors.Errorf("fetch disk by id %s", disk.Id)
- }
- ret := &hostapi.ContainerVolumeMountDisk{
- Index: disk.Index,
- Id: disk.Id,
- TemplateId: diskObj.TemplateId,
- SubDirectory: disk.SubDirectory,
- StorageSizeFile: disk.StorageSizeFile,
- Overlay: disk.Overlay,
- CaseInsensitivePaths: disk.CaseInsensitivePaths,
- PostOverlay: disk.PostOverlay,
- ResUid: disk.ResUid,
- ResGid: disk.ResGid,
- }
- return ret, nil
- }
- func (vm *ContainerVolumeMountRelation) toCephFSMount(fs *apis.ContainerVolumeMountCephFS) (*hostapi.ContainerVolumeMountCephFS, error) {
- fsObj, err := FileSystemManager.FetchById(fs.Id)
- if err != nil {
- return nil, errors.Errorf("fetch cephfs by id %s", fs.Id)
- }
- filesystem := fsObj.(*SFileSystem)
- ret := &hostapi.ContainerVolumeMountCephFS{
- Id: filesystem.Id,
- Path: filesystem.ExternalId,
- }
- account := filesystem.GetCloudaccount()
- if gotypes.IsNil(account) {
- return nil, fmt.Errorf("invalid cephfs %s", filesystem.Name)
- }
- ret.Secret, err = account.GetOptionPassword()
- if err != nil {
- return nil, err
- }
- ret.Name, _ = account.Options.GetString("name")
- if len(ret.Name) == 0 {
- ret.Name = "admin"
- }
- ret.MonHost, _ = account.Options.GetString("mon_host")
- return ret, nil
- }
- func (vm *ContainerVolumeMountRelation) ToHostMount(ctx context.Context, userCred mcclient.TokenCredential) (*hostapi.ContainerVolumeMount, error) {
- ret := &hostapi.ContainerVolumeMount{
- UniqueName: vm.VolumeMount.UniqueName,
- Type: vm.VolumeMount.Type,
- Disk: nil,
- CephFS: nil,
- Text: vm.VolumeMount.Text,
- HostPath: vm.VolumeMount.HostPath,
- ReadOnly: vm.VolumeMount.ReadOnly,
- MountPath: vm.VolumeMount.MountPath,
- SelinuxRelabel: vm.VolumeMount.SelinuxRelabel,
- Propagation: vm.VolumeMount.Propagation,
- FsUser: vm.VolumeMount.FsUser,
- FsGroup: vm.VolumeMount.FsGroup,
- }
- if vm.VolumeMount.Disk != nil {
- disk, err := vm.toHostDiskMount(vm.VolumeMount.Disk)
- if err != nil {
- return nil, errors.Wrap(err, "toHostDiskMount")
- }
- ret.Disk = disk
- }
- if vm.VolumeMount.CephFS != nil {
- fs, err := vm.toCephFSMount(vm.VolumeMount.CephFS)
- if err != nil {
- return nil, errors.Wrapf(err, "getCephFSSecret")
- }
- ret.CephFS = fs
- }
- return ret, nil
- }
- func (m *SContainerManager) GetRootFsRelation(pod *SGuest, spec *api.ContainerSpec) (*ContainerRootFsRelation, error) {
- return &ContainerRootFsRelation{
- RootFs: spec.RootFs,
- pod: pod,
- }, nil
- }
- func (m *SContainerManager) GetVolumeMountRelations(pod *SGuest, spec *api.ContainerSpec) ([]*ContainerVolumeMountRelation, error) {
- relation := make([]*ContainerVolumeMountRelation, len(spec.VolumeMounts))
- for idx, vm := range spec.VolumeMounts {
- tmpVm := vm
- relation[idx] = &ContainerVolumeMountRelation{
- VolumeMount: tmpVm,
- pod: pod,
- }
- }
- return relation, nil
- }
- func (c *SContainer) GetRootFsRelation() (*ContainerRootFsRelation, error) {
- return GetContainerManager().GetRootFsRelation(c.GetPod(), c.Spec)
- }
- func (c *SContainer) GetVolumeMountRelations() ([]*ContainerVolumeMountRelation, error) {
- return GetContainerManager().GetVolumeMountRelations(c.GetPod(), c.Spec)
- }
- func (c *SContainer) PerformStart(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- if !sets.NewString(api.CONTAINER_STATUS_EXITED, api.CONTAINER_STATUS_START_FAILED).Has(c.Status) {
- return nil, httperrors.NewInvalidStatusError("Can't start container in status %s", c.Status)
- }
- return nil, c.StartStartTask(ctx, userCred, "")
- }
- func (c *SContainer) StartStartTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
- c.SetStatus(ctx, userCred, api.CONTAINER_STATUS_STARTING, "")
- task, err := taskman.TaskManager.NewTask(ctx, "ContainerStartTask", c, userCred, nil, parentTaskId, "", nil)
- if err != nil {
- return errors.Wrap(err, "NewTask")
- }
- return task.ScheduleRun(nil)
- }
- func (c *SContainer) PerformStop(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *api.ContainerStopInput) (jsonutils.JSONObject, error) {
- if !data.Force {
- if !sets.NewString(
- api.CONTAINER_STATUS_RUNNING,
- api.CONTAINER_STATUS_PROBING,
- api.CONTAINER_STATUS_PROBE_FAILED,
- api.CONTAINER_STATUS_STOP_FAILED).Has(c.Status) {
- return nil, httperrors.NewInvalidStatusError("Can't stop container in status %s", c.Status)
- }
- }
- return nil, c.StartStopTask(ctx, userCred, data, "")
- }
- func (c *SContainer) StartStopTask(ctx context.Context, userCred mcclient.TokenCredential, data *api.ContainerStopInput, parentTaskId string) error {
- c.SetStatus(ctx, userCred, api.CONTAINER_STATUS_STOPPING, "")
- task, err := taskman.TaskManager.NewTask(ctx, "ContainerStopTask", c, userCred, jsonutils.Marshal(data).(*jsonutils.JSONDict), parentTaskId, "", nil)
- if err != nil {
- return errors.Wrap(err, "NewTask")
- }
- return task.ScheduleRun(nil)
- }
- func (c *SContainer) PerformRestart(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *api.ContainerRestartInput) (jsonutils.JSONObject, error) {
- if !data.Force {
- if !sets.NewString(
- api.CONTAINER_STATUS_RUNNING,
- api.CONTAINER_STATUS_PROBING,
- api.CONTAINER_STATUS_PROBE_FAILED,
- api.CONTAINER_STATUS_STOP_FAILED).Has(c.Status) {
- return nil, httperrors.NewInvalidStatusError("Can't restart container in status %s", c.Status)
- }
- }
- return nil, c.StartRestartTask(ctx, userCred, data, "")
- }
- func (c *SContainer) StartRestartTask(ctx context.Context, userCred mcclient.TokenCredential, data *api.ContainerRestartInput, parentTaskId string) error {
- task, err := taskman.TaskManager.NewTask(ctx, "ContainerRestartTask", c, userCred, jsonutils.Marshal(data).(*jsonutils.JSONDict), parentTaskId, "", nil)
- if err != nil {
- return errors.Wrap(err, "NewTask")
- }
- return task.ScheduleRun(nil)
- }
- func (c *SContainer) PerformSyncstatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- return nil, c.StartSyncStatusTask(ctx, userCred, "")
- }
- func (c *SContainer) StartSyncStatusTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
- c.SetStatus(ctx, userCred, api.CONTAINER_STATUS_SYNC_STATUS, "")
- task, err := taskman.TaskManager.NewTask(ctx, "ContainerSyncStatusTask", c, userCred, nil, parentTaskId, "", nil)
- if err != nil {
- return errors.Wrap(err, "NewTask")
- }
- return task.ScheduleRun(nil)
- }
- func (c *SContainer) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query, data jsonutils.JSONObject) error {
- return c.StartDeleteTask(ctx, userCred, "", jsonutils.QueryBoolean(data, "purge", false))
- }
- func (c *SContainer) StartDeleteTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string, purge bool) error {
- c.SetStatus(ctx, userCred, api.CONTAINER_STATUS_DELETING, "")
- task, err := taskman.TaskManager.NewTask(ctx, "ContainerDeleteTask", c, userCred, jsonutils.Marshal(map[string]interface{}{
- "purge": purge,
- }).(*jsonutils.JSONDict), parentTaskId, "", nil)
- if err != nil {
- return errors.Wrap(err, "NewTask")
- }
- return task.ScheduleRun(nil)
- }
- func (m *SContainerManager) getKeystoneCredential(ctx context.Context, userCred mcclient.TokenCredential, id string) (jsonutils.JSONObject, error) {
- s := auth.GetSession(ctx, userCred, options.Options.Region)
- if cred, err := identitymod.Credentials.GetById(s, id, nil); err == nil {
- return cred, nil
- } else if errors.Cause(err) == errors.ErrNotFound || strings.Contains(err.Error(), "NotFound") {
- cred2, err2 := identitymod.Credentials.GetByName(s, id, nil)
- if err2 != nil {
- return nil, errors.Wrapf(err2, "get credential by id or name of %s", id)
- }
- return cred2, nil
- } else {
- return nil, errors.Wrapf(err, "get credentials by id with %s", userCred.String())
- }
- }
- func (m *SContainerManager) parseKeystoneCredentialBlob(ret jsonutils.JSONObject, expectedType string) (jsonutils.JSONObject, error) {
- credType, _ := ret.GetString("type")
- if credType != expectedType {
- return nil, httperrors.NewNotSupportedError("unsupported credential type %s", credType)
- }
- blobStr, err := ret.GetString("blob")
- if err != nil {
- return nil, errors.Wrap(err, "get blob")
- }
- obj, err := jsonutils.ParseString(blobStr)
- if err != nil {
- return nil, errors.Wrapf(err, "json parse string: %s", blobStr)
- }
- return obj, nil
- }
- func (m *SContainerManager) GetImageCredential(ctx context.Context, userCred mcclient.TokenCredential, id string) (*apis.ContainerPullImageAuthConfig, error) {
- ret, err := m.getKeystoneCredential(ctx, userCred, id)
- if err != nil {
- return nil, err
- }
- obj, err := m.parseKeystoneCredentialBlob(ret, identityapi.CONTAINER_IMAGE_TYPE)
- if err != nil {
- return nil, err
- }
- blob := new(identityapi.CredentialContainerImageBlob)
- if err := obj.Unmarshal(blob); err != nil {
- return nil, errors.Wrap(err, "unmarshal blob")
- }
- out := &apis.ContainerPullImageAuthConfig{
- Username: blob.Username,
- Password: blob.Password,
- Auth: blob.Auth,
- ServerAddress: blob.ServerAddress,
- IdentityToken: blob.IdentityToken,
- RegistryToken: blob.RegistryToken,
- }
- return out, nil
- }
- func (m *SContainerManager) GetSecretCredential(ctx context.Context, userCred mcclient.TokenCredential, id string) (map[string]string, error) {
- ret, err := m.getKeystoneCredential(ctx, userCred, id)
- if err != nil {
- return nil, err
- }
- obj, err := m.parseKeystoneCredentialBlob(ret, identityapi.CONTAINER_SECRET_TYPE)
- if err != nil {
- return nil, err
- }
- out := map[string]string{}
- if err := obj.Unmarshal(&out); err != nil {
- return nil, errors.Wrap(err, "unmarshal blob")
- }
- return out, nil
- }
- func (c *SContainer) GetImageCredential(ctx context.Context, userCred mcclient.TokenCredential) (*apis.ContainerPullImageAuthConfig, error) {
- if c.Spec.ImageCredentialId == "" {
- return nil, errors.Wrap(errors.ErrEmpty, "image_credential_id is empty")
- }
- return GetContainerManager().GetImageCredential(ctx, userCred, c.Spec.ImageCredentialId)
- }
- func (c *SContainer) GetHostPullImageInput(ctx context.Context, userCred mcclient.TokenCredential) (*hostapi.ContainerPullImageInput, error) {
- input := &hostapi.ContainerPullImageInput{
- Image: c.Spec.Image,
- PullPolicy: c.Spec.ImagePullPolicy,
- }
- if c.Spec.ImageCredentialId != "" {
- cred, err := c.GetImageCredential(ctx, userCred)
- if err != nil {
- return nil, errors.Wrapf(err, "GetImageCredential %s", c.Spec.ImageCredentialId)
- }
- input.Auth = cred
- }
- return input, nil
- }
- func (c *SContainer) GetSecretCredentials(ctx context.Context, userCred mcclient.TokenCredential) (map[string]string, error) {
- ret := make(map[string]string, 0)
- for _, env := range c.Spec.Envs {
- if env.ValueFrom == nil {
- continue
- }
- if env.ValueFrom.Credential != nil {
- credId := env.ValueFrom.Credential.Id
- cred, err := GetContainerManager().GetSecretCredential(ctx, userCred, credId)
- if err != nil {
- return nil, errors.Wrapf(err, "GetSecretCredential %s", credId)
- }
- ret[credId] = base64.StdEncoding.EncodeToString([]byte(jsonutils.Marshal(cred).String()))
- }
- }
- return ret, nil
- }
- func (c *SContainer) StartPullImageTask(ctx context.Context, userCred mcclient.TokenCredential, input *hostapi.ContainerPullImageInput, parentTaskId string) error {
- c.SetStatus(ctx, userCred, api.CONTAINER_STATUS_PULLING_IMAGE, "")
- task, err := taskman.TaskManager.NewTask(ctx, "ContainerPullImageTask", c, userCred, jsonutils.Marshal(input).(*jsonutils.JSONDict), parentTaskId, "", nil)
- if err != nil {
- return errors.Wrap(err, "NewTask")
- }
- return task.ScheduleRun(nil)
- }
- func (c *SContainer) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
- return c.SVirtualResourceBase.Delete(ctx, userCred)
- }
- func (m *SContainerManager) ConvertVolumeMountRelationToSpec(ctx context.Context, userCred mcclient.TokenCredential, relation []*ContainerVolumeMountRelation) ([]*hostapi.ContainerVolumeMount, error) {
- mounts := make([]*hostapi.ContainerVolumeMount, 0)
- for _, r := range relation {
- mount, err := r.ToHostMount(ctx, userCred)
- if err != nil {
- return nil, errors.Wrapf(err, "ToMountOrDevice: %#v", r)
- }
- if mount != nil {
- mounts = append(mounts, mount)
- }
- }
- return mounts, nil
- }
- func (m *SContainerManager) ConvertRootFsRelationToSpec(ctx context.Context, userCred mcclient.TokenCredential, relation *ContainerRootFsRelation) (*hostapi.ContainerRootfs, error) {
- if relation.RootFs == nil {
- return nil, nil
- }
- rootFs, err := relation.ToHostRootFs()
- if err != nil {
- return nil, errors.Wrap(err, "ToHostRootFs")
- }
- return rootFs, nil
- }
- func (c *SContainer) ToHostContainerSpec(ctx context.Context, userCred mcclient.TokenCredential) (*hostapi.ContainerSpec, error) {
- rootFsRelation, err := c.GetRootFsRelation()
- if err != nil {
- return nil, errors.Wrap(err, "GetRootFsRelation")
- }
- rootFs, err := GetContainerManager().ConvertRootFsRelationToSpec(ctx, userCred, rootFsRelation)
- if err != nil {
- return nil, errors.Wrap(err, "ConvertRootFsRelationToSpec")
- }
- vmRelation, err := c.GetVolumeMountRelations()
- if err != nil {
- return nil, errors.Wrap(err, "GetVolumeMountRelations")
- }
- mounts, err := GetContainerManager().ConvertVolumeMountRelationToSpec(ctx, userCred, vmRelation)
- if err != nil {
- return nil, errors.Wrap(err, "ConvertVolumeRelationToSpec")
- }
- ctrDevs := make([]*hostapi.ContainerDevice, 0)
- for _, dev := range c.Spec.Devices {
- ctrDev, err := GetContainerDeviceDriver(dev.Type).ToHostDevice(dev)
- if err != nil {
- return nil, errors.Wrapf(err, "ToHostDevice %s", jsonutils.Marshal(dev))
- }
- ctrDevs = append(ctrDevs, ctrDev)
- }
- spec := c.Spec.ContainerSpec
- hSpec := &hostapi.ContainerSpec{
- ContainerSpec: spec,
- Rootfs: rootFs,
- VolumeMounts: mounts,
- Devices: ctrDevs,
- }
- pullInput, err := c.GetHostPullImageInput(ctx, userCred)
- if err != nil {
- return nil, errors.Wrap(err, "GetHostPullImageInput")
- }
- hSpec.ImageCredentialToken = base64.StdEncoding.EncodeToString([]byte(jsonutils.Marshal(pullInput.Auth).String()))
- secretCredentials, err := c.GetSecretCredentials(ctx, userCred)
- if err != nil {
- return nil, errors.Wrap(err, "GetSecretCredentials")
- }
- hSpec.SecretCredentials = secretCredentials
- return hSpec, nil
- }
- func (c *SContainer) GetJsonDescAtHost(ctx context.Context, userCred mcclient.TokenCredential) (*hostapi.ContainerDesc, error) {
- spec, err := c.ToHostContainerSpec(ctx, userCred)
- if err != nil {
- return nil, errors.Wrap(err, "ToHostContainerSpec")
- }
- return &hostapi.ContainerDesc{
- Id: c.GetId(),
- Name: c.GetName(),
- Spec: spec,
- StartedAt: c.StartedAt,
- LastFinishedAt: c.LastFinishedAt,
- RestartCount: c.RestartCount,
- }, nil
- }
- func (c *SContainer) PrepareSaveImage(ctx context.Context, userCred mcclient.TokenCredential, input *api.ContainerSaveVolumeMountToImageInput) (string, error) {
- imageInput := &CreateGlanceImageInput{
- Name: input.Name,
- GenerateName: input.GenerateName,
- DiskFormat: imageapi.IMAGE_DISK_FORMAT_TGZ,
- Properties: map[string]string{
- "notes": input.Notes,
- },
- // inherit the ownership of disk
- ProjectId: c.ProjectId,
- }
- if len(input.Dirs) > 0 {
- dirMap := make(map[string]string, 0)
- for _, dir := range input.Dirs {
- dirMap[dir] = dir
- }
- imageInput.Properties[imageapi.IMAGE_INTERNAL_PATH_MAP] = jsonutils.Marshal(dirMap).String()
- }
- if input.UsedByPostOverlay {
- imageInput.Properties[imageapi.IMAGE_USED_BY_POST_OVERLAY] = jsonutils.Marshal(input.UsedByPostOverlay).String()
- }
- // check class metadata
- cm, err := c.GetAllClassMetadata()
- if err != nil {
- return "", errors.Wrap(err, "unable to GetAllClassMetadata")
- }
- imageInput.ClassMetadata = cm
- return DiskManager.CreateGlanceImage(ctx, userCred, imageInput)
- }
- func (c *SContainer) PerformSaveVolumeMountImage(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.ContainerSaveVolumeMountToImageInput) (*hostapi.ContainerSaveVolumeMountToImageInput, error) {
- if c.GetStatus() != api.CONTAINER_STATUS_EXITED {
- return nil, httperrors.NewInvalidStatusError("Can't save volume disk of container in status %s", c.Status)
- }
- if c.GetPod().GetStatus() != api.VM_READY {
- return nil, httperrors.NewInvalidStatusError("Can't save volume disk of pod in status %s", c.GetPod().GetStatus())
- }
- vols := c.GetVolumeMounts()
- if input.Index < 0 || input.Index >= len(vols) {
- return nil, httperrors.NewInputParameterError("Only %d volume_mounts", len(vols))
- }
- imageId, err := c.PrepareSaveImage(ctx, userCred, input)
- if err != nil {
- return nil, errors.Wrap(err, "prepare to save image")
- }
- vrs, err := c.GetVolumeMountRelations()
- if err != nil {
- return nil, errors.Wrap(err, "GetVolumeMountRelations")
- }
- hvm, err := vrs[input.Index].ToHostMount(ctx, userCred)
- if err != nil {
- return nil, errors.Wrap(err, "ToHostMount")
- }
- cleanupDirPath := func(dirPath string) string {
- nPath := ""
- pathSegs := strings.Split(dirPath, "/")
- for _, seg := range pathSegs {
- if len(seg) > 0 {
- nPath = filepath.Join(nPath, seg)
- }
- }
- return nPath
- }
- cleanupDirPaths := func(dirPaths []string) []string {
- for i := range dirPaths {
- dirPaths[i] = cleanupDirPath(dirPaths[i])
- }
- return dirPaths
- }
- hostInput := &hostapi.ContainerSaveVolumeMountToImageInput{
- ImageId: imageId,
- VolumeMountIndex: input.Index,
- VolumeMount: hvm,
- VolumeMountDirs: cleanupDirPaths(input.Dirs),
- VolumeMountPrefix: cleanupDirPath(input.DirPrefix),
- ExcludePaths: cleanupDirPaths(input.ExcludePaths),
- }
- return hostInput, c.StartSaveVolumeMountImage(ctx, userCred, hostInput, "")
- }
- func (c *SContainer) StartSaveVolumeMountImage(ctx context.Context, userCred mcclient.TokenCredential, input *hostapi.ContainerSaveVolumeMountToImageInput, parentTaskId string) error {
- c.SetStatus(ctx, userCred, api.CONTAINER_STATUS_SAVING_IMAGE, "")
- task, err := taskman.TaskManager.NewTask(ctx, "ContainerSaveVolumeMountImageTask", c, userCred, jsonutils.Marshal(input).(*jsonutils.JSONDict), parentTaskId, "", nil)
- if err != nil {
- return errors.Wrap(err, "NewTask")
- }
- return task.ScheduleRun(nil)
- }
- func (c *SContainer) GetPodDriver() IPodDriver {
- driver, err := c.GetPod().GetDriver()
- if err != nil {
- return nil
- }
- return driver.(IPodDriver)
- }
- func (c *SContainer) GetDetailsExecInfo(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*api.ContainerExecInfoOutput, error) {
- gst := c.GetPod()
- host, err := gst.GetHost()
- if err != nil {
- return nil, errors.Wrap(err, "GetHost")
- }
- out := &api.ContainerExecInfoOutput{
- HostUri: host.ManagerUri,
- PodId: c.GuestId,
- ContainerId: c.Id,
- }
- return out, nil
- }
- func (c *SContainer) PerformExecSync(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.ContainerExecSyncInput) (jsonutils.JSONObject, error) {
- if sets.NewString(
- api.CONTAINER_STATUS_EXITED,
- api.CONTAINER_STATUS_CREATED,
- ).Has(c.GetStatus()) {
- return nil, httperrors.NewInvalidStatusError("Can't exec container in status %s", c.Status)
- }
- return c.GetPodDriver().RequestExecSyncContainer(ctx, userCred, c, input)
- }
- func (c *SContainer) PerformSetResourcesLimit(ctx context.Context, userCred mcclient.TokenCredential, _ jsonutils.JSONObject, input *api.ContainerResourcesSetInput) (jsonutils.JSONObject, error) {
- limit := &input.ContainerResources
- if err := c.ValidateResourcesLimit(limit, input.DisableLimitCheck); err != nil {
- return nil, errors.Wrap(err, "ValidateResourcesLimit")
- }
- if _, err := db.Update(c, func() error {
- c.Spec.ResourcesLimit = limit
- return nil
- }); err != nil {
- return nil, errors.Wrap(err, "Update spec.resources_limit")
- }
- if !api.ContainerRunningStatus.Has(c.GetStatus()) {
- return nil, nil
- }
- return c.GetPodDriver().RequestSetContainerResourcesLimit(ctx, userCred, c, limit)
- }
- func (c *SContainer) ValidateResourcesLimit(limit *apis.ContainerResources, disableLimitCheck bool) error {
- if limit == nil {
- return httperrors.NewInputParameterError("limit cannot be nil")
- }
- pod := c.GetPod()
- if limit.CpuCfsQuota != nil {
- if *limit.CpuCfsQuota <= 0 {
- return httperrors.NewInputParameterError("invalid cpu_cfs_quota %f", *limit.CpuCfsQuota)
- }
- if !disableLimitCheck {
- if *limit.CpuCfsQuota > float64(pod.VcpuCount) {
- return httperrors.NewInputParameterError("cpu_cfs_quota %f can't large than %d", *limit.CpuCfsQuota, pod.VcpuCount)
- }
- }
- }
- return nil
- }
- type ContainerReleasedDevice struct {
- *api.ContainerDevice
- DeviceType string
- DeviceModel string
- }
- func NewContainerReleasedDevice(device *api.ContainerDevice, devType, devModel string) *ContainerReleasedDevice {
- return &ContainerReleasedDevice{
- ContainerDevice: device,
- DeviceType: devType,
- DeviceModel: devModel,
- }
- }
- func (c *SContainer) SaveReleasedDevices(ctx context.Context, userCred mcclient.TokenCredential, devs map[string]ContainerReleasedDevice) error {
- return c.SetMetadata(ctx, api.CONTAINER_METADATA_RELEASED_DEVICES, devs, userCred)
- }
- func (c *SContainer) GetReleasedDevices(ctx context.Context, userCred mcclient.TokenCredential) (map[string]ContainerReleasedDevice, error) {
- out := make(map[string]ContainerReleasedDevice, 0)
- if ret := c.GetMetadata(ctx, api.CONTAINER_METADATA_RELEASED_DEVICES, userCred); ret == "" {
- return out, nil
- }
- obj := c.GetMetadataJson(ctx, api.CONTAINER_METADATA_RELEASED_DEVICES, userCred)
- if obj == nil {
- return nil, errors.Error("get metadata released devices")
- }
- if err := obj.Unmarshal(&out); err != nil {
- return nil, errors.Wrap(err, "Unmarshal metadata released devices")
- }
- return out, nil
- }
- func (c *SContainer) PerformStatus(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ContainerPerformStatusInput) (jsonutils.JSONObject, error) {
- if api.ContainerExitedStatus.Has(c.GetStatus()) {
- if sets.NewString(api.CONTAINER_STATUS_PROBE_FAILED, api.CONTAINER_STATUS_NET_FAILED).Has(input.Status) {
- return nil, httperrors.NewInputParameterError("can't set container status to %s when %s", input.Status, c.Status)
- }
- }
- if _, err := db.Update(c, func() error {
- if input.RestartCount > 0 {
- c.RestartCount = input.RestartCount
- }
- if api.ContainerRunningStatus.Has(input.Status) {
- // 当容器状态是运行时 restart_count 重新计数
- c.RestartCount = 0
- }
- if input.StartedAt != nil {
- c.StartedAt = *input.StartedAt
- }
- if input.LastFinishedAt != nil {
- c.LastFinishedAt = *input.LastFinishedAt
- }
- return nil
- }); err != nil {
- return nil, errors.Wrap(err, "Update container status")
- }
- return c.SVirtualResourceBase.PerformStatus(ctx, userCred, query, input.PerformStatusInput)
- }
- func (c *SContainer) getContainerHostCommitInput(ctx context.Context, userCred mcclient.TokenCredential, input *api.ContainerCommitInput) (*hostapi.ContainerCommitInput, error) {
- var hostInput = &hostapi.ContainerCommitInput{
- Auth: new(apis.ContainerPullImageAuthConfig),
- }
- var repoUrl string
- imageName := input.ImageName
- tag := input.Tag
- if imageName == "" {
- imageName = c.GetName()
- }
- if tag == "" {
- tag = time.Now().Format("20060102150405")
- }
- if input.RegistryId != "" {
- s := auth.GetSession(ctx, userCred, consts.GetRegion())
- obj, err := kubemod.ContainerRegistries.Get(s, input.RegistryId, nil)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- reg := new(api.KubeServerContainerRegistryDetails)
- if err := obj.Unmarshal(reg); err != nil {
- return nil, errors.Wrap(err, "Unmarshal kube server registry details")
- }
- if reg.Config == nil {
- confObj, err := kubemod.ContainerRegistries.GetSpecific(s, input.RegistryId, "config", nil)
- if err != nil {
- return nil, errors.Wrap(err, "Get kube server registry config")
- }
- reg.Config = new(api.KubeServerContainerRegistryConfig)
- if err := confObj.Unmarshal(reg.Config); err != nil {
- return nil, errors.Wrap(err, "Unmarshal kube server registry config")
- }
- }
- repoUrl = reg.Url
- if reg.Config != nil {
- switch reg.Type {
- case "common":
- cfg := reg.Config.Common
- hostInput.Auth.Username = cfg.Username
- hostInput.Auth.Password = cfg.Password
- case "harbor":
- cfg := reg.Config.Harbor
- hostInput.Auth.Username = cfg.Username
- hostInput.Auth.Password = cfg.Password
- default:
- return nil, httperrors.NewInputParameterError("invalid registry type %s", reg.Type)
- }
- }
- } else if input.ExternalRegistry != nil {
- repoUrl = input.ExternalRegistry.Url
- if repoUrl == "" {
- return nil, httperrors.NewNotEmptyError("empty external registry url")
- }
- hostInput.Auth = input.ExternalRegistry.Auth
- } else {
- return nil, httperrors.NewInputParameterError("one of registry_id or external_registry must provided")
- }
- repoPrefix := strings.TrimPrefix(strings.TrimPrefix(repoUrl, "http://"), "https://")
- hostInput.Repository = fmt.Sprintf("%s:%s", filepath.Join(repoPrefix, imageName), tag)
- return hostInput, nil
- }
- func (c *SContainer) PerformCommit(ctx context.Context, userCred mcclient.TokenCredential, _ jsonutils.JSONObject, input *api.ContainerCommitInput) (*api.ContainerCommitOutput, error) {
- hostInput, err := c.getContainerHostCommitInput(ctx, userCred, input)
- if err != nil {
- return nil, err
- }
- out := &api.ContainerCommitOutput{
- Repository: hostInput.Repository,
- }
- return out, c.StartCommit(ctx, userCred, hostInput, "")
- }
- func (c *SContainer) StartCommit(ctx context.Context, userCred mcclient.TokenCredential, input *hostapi.ContainerCommitInput, parentTaskId string) error {
- c.SetStatus(ctx, userCred, api.CONTAINER_STATUS_COMMITTING, "")
- task, err := taskman.TaskManager.NewTask(ctx, "ContainerCommitTask", c, userCred, jsonutils.Marshal(input).(*jsonutils.JSONDict), parentTaskId, "", nil)
- if err != nil {
- return errors.Wrap(err, "NewTask")
- }
- return task.ScheduleRun(nil)
- }
- func (c *SContainer) isPostOverlayExist(vm *apis.ContainerVolumeMount, ov *apis.ContainerVolumeMountDiskPostOverlay) (*apis.ContainerVolumeMountDiskPostOverlay, bool) {
- for i := range vm.Disk.PostOverlay {
- cov := vm.Disk.PostOverlay[i]
- if cov.IsEqual(*ov) {
- return cov, true
- }
- }
- return nil, false
- }
- func (c *SContainer) validateVolumeMountPostOverlayAction(action string, index int) (*apis.ContainerVolumeMount, error) {
- if !api.ContainerExitedStatus.Has(c.Status) && !api.ContainerRunningStatus.Has(c.Status) {
- return nil, httperrors.NewInvalidStatusError("can't %s post overlay on status %s", action, c.Status)
- }
- if index >= len(c.Spec.VolumeMounts) {
- return nil, httperrors.NewInputParameterError("index %d out of volume_mount size %d", index, len(c.Spec.VolumeMounts))
- }
- vm := new(apis.ContainerVolumeMount)
- curVm := c.Spec.VolumeMounts[index]
- if err := jsonutils.Marshal(curVm).Unmarshal(vm); err != nil {
- return nil, errors.Wrap(err, "use json unmarshal to new volume mount")
- }
- if vm.Type != apis.CONTAINER_VOLUME_MOUNT_TYPE_DISK {
- return nil, httperrors.NewInputParameterError("invalid volume mount type %s", vm.Type)
- }
- return vm, nil
- }
- func (c *SContainer) GetVolumeMountCopy(index int) (*apis.ContainerVolumeMount, error) {
- if index >= len(c.Spec.VolumeMounts) {
- return nil, httperrors.NewInputParameterError("index %d out of volume_mount size %d", index, len(c.Spec.VolumeMounts))
- }
- vm := new(apis.ContainerVolumeMount)
- curVm := c.Spec.VolumeMounts[index]
- if err := jsonutils.Marshal(curVm).Unmarshal(vm); err != nil {
- return nil, errors.Wrap(err, "use json unmarshal to new volume mount")
- }
- return vm, nil
- }
- func (c *SContainer) getPostOverlayVolumeMount(
- index int,
- updateF func(mount *apis.ContainerVolumeMount) (*apis.ContainerVolumeMount, error),
- ) (*apis.ContainerVolumeMount, error) {
- vm, err := c.GetVolumeMountCopy(index)
- if err != nil {
- return nil, err
- }
- return updateF(vm)
- }
- func (c *SContainer) GetAddPostOverlayVolumeMount(index int, ovs []*apis.ContainerVolumeMountDiskPostOverlay) (*apis.ContainerVolumeMount, error) {
- return c.getPostOverlayVolumeMount(index, func(vm *apis.ContainerVolumeMount) (*apis.ContainerVolumeMount, error) {
- if vm.Disk.PostOverlay == nil {
- vm.Disk.PostOverlay = []*apis.ContainerVolumeMountDiskPostOverlay{}
- }
- vm.Disk.PostOverlay = append(vm.Disk.PostOverlay, ovs...)
- return vm, nil
- })
- }
- func (c *SContainer) GetRemovePostOverlayVolumeMount(index int, ovs []*apis.ContainerVolumeMountDiskPostOverlay) (*apis.ContainerVolumeMount, error) {
- return c.getPostOverlayVolumeMount(index, func(vm *apis.ContainerVolumeMount) (*apis.ContainerVolumeMount, error) {
- // remove post overlay
- for _, ov := range ovs {
- vm.Disk = c.removePostOverlay(vm.Disk, ov)
- }
- return vm, nil
- })
- }
- func (c *SContainer) PerformAddVolumeMountPostOverlay(ctx context.Context, userCred mcclient.TokenCredential, _ jsonutils.JSONObject, input *api.ContainerVolumeMountAddPostOverlayInput) (jsonutils.JSONObject, error) {
- vm, err := c.validateVolumeMountPostOverlayAction("add", input.Index)
- if err != nil {
- return nil, err
- }
- totalOvs := []*apis.ContainerVolumeMountDiskPostOverlay{}
- totalOvs = append(totalOvs, vm.Disk.PostOverlay...)
- drv := GetContainerVolumeMountDriver(vm.Type)
- dDrv := drv.(IContainerVolumeMountDiskDriver)
- for i := range input.PostOverlay {
- ov := input.PostOverlay[i]
- cov, isExist := c.isPostOverlayExist(vm, ov)
- if isExist {
- return nil, httperrors.NewInputParameterError("post overlay already exists: %s", jsonutils.Marshal(cov))
- }
- if err := dDrv.ValidatePostSingleOverlay(ctx, userCred, ov); err != nil {
- return nil, errors.Wrapf(err, "validate post overlay %s", jsonutils.Marshal(ov))
- }
- totalOvs = append(totalOvs, ov)
- }
- if err := dDrv.ValidatePostOverlayTargetDirs(totalOvs); err != nil {
- return nil, errors.Wrapf(err, "validate container target dirs")
- }
- return nil, c.StartAddVolumeMountPostOverlayTask(ctx, userCred, input, "")
- }
- func (c *SContainer) StartCacheImagesTask(ctx context.Context, userCred mcclient.TokenCredential, input *api.ContainerCacheImagesInput, parentTaskId string) error {
- c.SetStatus(ctx, userCred, api.CONTAINER_STATUS_CACHE_IMAGE, "")
- task, err := taskman.TaskManager.NewTask(ctx, "ContainerCacheImagesTask", c, userCred, jsonutils.Marshal(input).(*jsonutils.JSONDict), parentTaskId, "", nil)
- if err != nil {
- return errors.Wrap(err, "New ContainerCacheImagesTask")
- }
- return task.ScheduleRun(nil)
- }
- func (c *SContainer) StartAddVolumeMountPostOverlayTask(ctx context.Context, userCred mcclient.TokenCredential, input *api.ContainerVolumeMountAddPostOverlayInput, parentTaskId string) error {
- c.SetStatus(ctx, userCred, api.CONTAINER_STATUS_ADD_POST_OVERLY, "")
- task, err := taskman.TaskManager.NewTask(ctx, "ContainerAddVolumeMountPostOverlayTask", c, userCred, jsonutils.Marshal(input).(*jsonutils.JSONDict), parentTaskId, "", nil)
- if err != nil {
- return errors.Wrap(err, "New ContainerAddVolumeMountPostOverlayTask")
- }
- return task.ScheduleRun(nil)
- }
- func (c *SContainer) StartRemoveVolumeMountPostOverlayTask(ctx context.Context, userCred mcclient.TokenCredential, input *api.ContainerVolumeMountRemovePostOverlayInput, parentTaskId string) error {
- c.SetStatus(ctx, userCred, api.CONTAINER_STATUS_REMOVE_POST_OVERLY, "")
- task, err := taskman.TaskManager.NewTask(ctx, "ContainerRemoveVolumeMountPostOverlayTask", c, userCred, jsonutils.Marshal(input).(*jsonutils.JSONDict), parentTaskId, "", nil)
- if err != nil {
- return errors.Wrap(err, "New ContainerRemoveVolumeMountPostOverlayTask")
- }
- return task.ScheduleRun(nil)
- }
- func (c *SContainer) PerformRemoveVolumeMountPostOverlay(ctx context.Context, userCred mcclient.TokenCredential, _ jsonutils.JSONObject, input *api.ContainerVolumeMountRemovePostOverlayInput) (jsonutils.JSONObject, error) {
- vm, err := c.validateVolumeMountPostOverlayAction("remove", input.Index)
- if err != nil {
- return nil, err
- }
- if len(vm.Disk.PostOverlay) == 0 {
- return nil, httperrors.NewInputParameterError("no post overlay")
- }
- for i, ov := range input.PostOverlay {
- cov, isExist := c.isPostOverlayExist(vm, ov)
- if !isExist {
- return nil, httperrors.NewInputParameterError("post overlay not exists: %s", jsonutils.Marshal(ov))
- }
- input.PostOverlay[i] = cov
- }
- return nil, c.StartRemoveVolumeMountPostOverlayTask(ctx, userCred, input, "")
- }
- func (c *SContainer) removePostOverlay(vmd *apis.ContainerVolumeMountDisk, ov *apis.ContainerVolumeMountDiskPostOverlay) *apis.ContainerVolumeMountDisk {
- curOvs := vmd.PostOverlay
- resultOvs := []*apis.ContainerVolumeMountDiskPostOverlay{}
- for i := range curOvs {
- cov := curOvs[i]
- if cov.IsEqual(*ov) {
- continue
- }
- resultOvs = append(resultOvs, cov)
- }
- vmd.PostOverlay = resultOvs
- return vmd
- }
- func (c *SContainer) SetStatusFromHost(ctx context.Context, userCred mcclient.TokenCredential, resp api.ContainerSyncStatusResponse, reason string) error {
- errs := []error{}
- if err := c.SetStatus(ctx, userCred, resp.Status, reason); err != nil {
- errs = append(errs, errors.Wrap(err, "SetStatus"))
- }
- if _, err := db.Update(c, func() error {
- if resp.RestartCount > 0 {
- c.RestartCount = resp.RestartCount
- }
- c.StartedAt = resp.StartedAt
- return nil
- }); err != nil {
- errs = append(errs, errors.Wrap(err, "Update container started_at: %s"))
- }
- return errors.NewAggregate(errs)
- }
|