mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-10-30 18:23:35 +08:00
feat(region,host): commit container to image repository (#21284)
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
FROM registry.cn-beijing.aliyuncs.com/yunionio/host-base:v0.5.0
|
||||
FROM registry.cn-beijing.aliyuncs.com/yunionio/host-base:v0.6.0
|
||||
|
||||
MAINTAINER "Yaoqi Wan wanyaoqi@yunionyun.com"
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ COPY . /root/go/src/yunion.io/x/onecloud
|
||||
WORKDIR /root/go/src/yunion.io/x/onecloud
|
||||
RUN make cmd/host
|
||||
|
||||
FROM registry.cn-beijing.aliyuncs.com/yunionio/host-base:v0.4.0
|
||||
FROM registry.cn-beijing.aliyuncs.com/yunionio/host-base:v0.6.0
|
||||
|
||||
MAINTAINER "Yaoqi Wan wanyaoqi@yunionyun.com"
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ func init() {
|
||||
cmd.Perform("save-volume-mount-image", new(options.ContainerSaveVolumeMountImage))
|
||||
cmd.Perform("exec-sync", new(options.ContainerExecSyncOptions))
|
||||
cmd.BatchPerform("set-resources-limit", new(options.ContainerSetResourcesLimitOptions))
|
||||
cmd.Perform("commit", new(options.ContainerCommitOptions))
|
||||
|
||||
type UpdateSpecOptions struct {
|
||||
ID string `help:"ID or name of server" json:"-"`
|
||||
|
||||
@@ -74,6 +74,8 @@ const (
|
||||
CONTAINER_STATUS_RUNNING = "running"
|
||||
CONTAINER_STATUS_DELETING = "deleting"
|
||||
CONTAINER_STATUS_DELETE_FAILED = "delete_failed"
|
||||
CONTAINER_STATUS_COMMITTING = "committing"
|
||||
CONTAINER_STATUS_COMMIT_FAILED = "commit_failed"
|
||||
// for health check
|
||||
CONTAINER_STATUS_PROBING = "probing"
|
||||
CONTAINER_STATUS_PROBE_FAILED = "probe_failed"
|
||||
@@ -185,3 +187,47 @@ type ContainerExecSyncResponse struct {
|
||||
Stderr string `json:"stderr"`
|
||||
ExitCode int32 `json:"exit_code"`
|
||||
}
|
||||
|
||||
type ContainerCommitExternalRegistry struct {
|
||||
// e.g.: registry.cn-beijing.aliyuncs.com/yunionio
|
||||
Url string `json:"url"`
|
||||
// authentication configuration
|
||||
Auth *apis.ContainerPullImageAuthConfig `json:"auth"`
|
||||
}
|
||||
|
||||
type ContainerCommitInput struct {
|
||||
// Container registry id from kubeserver
|
||||
RegistryId string `json:"registry_id"`
|
||||
ExternalRegistry *ContainerCommitExternalRegistry `json:"external_registry"`
|
||||
// image name
|
||||
ImageName string `json:"image_name"`
|
||||
// image tag
|
||||
Tag string `json:"tag"`
|
||||
}
|
||||
|
||||
type ContainerCommitOutput struct {
|
||||
Repository string `json:"repository"`
|
||||
}
|
||||
|
||||
type KubeServerContainerRegistryConfigCommon struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type KubeServerContainerRegistryConfigHarbor struct {
|
||||
KubeServerContainerRegistryConfigCommon
|
||||
}
|
||||
|
||||
type KubeServerContainerRegistryConfig struct {
|
||||
Type string `json:"type"`
|
||||
Common *KubeServerContainerRegistryConfigCommon `json:"common"`
|
||||
Harbor *KubeServerContainerRegistryConfigHarbor `json:"harbor"`
|
||||
}
|
||||
|
||||
type KubeServerContainerRegistryDetails struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Url string `json:"url"`
|
||||
Type string `json:"type"`
|
||||
Config *KubeServerContainerRegistryConfig `json:"config"`
|
||||
}
|
||||
|
||||
@@ -230,3 +230,15 @@ type ContainerVolumeMountText struct {
|
||||
type ContainerVolumeMountCephFS struct {
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
type ContainerPullImageAuthConfig struct {
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Auth string `json:"auth,omitempty"`
|
||||
ServerAddress string `json:"server_address,omitempty"`
|
||||
// IdentityToken is used to authenticate the user and get
|
||||
// an access token for the registry.
|
||||
IdentityToken string `json:"identity_token,omitempty"`
|
||||
// RegistryToken is a bearer token to be sent to a registry
|
||||
RegistryToken string `json:"registry_token,omitempty"`
|
||||
}
|
||||
|
||||
+12
-14
@@ -91,22 +91,15 @@ type ContainerCreateInput struct {
|
||||
Spec *ContainerSpec `json:"spec"`
|
||||
}
|
||||
|
||||
type ContainerPullImageAuthConfig struct {
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Auth string `json:"auth,omitempty"`
|
||||
ServerAddress string `json:"server_address,omitempty"`
|
||||
// IdentityToken is used to authenticate the user and get
|
||||
// an access token for the registry.
|
||||
IdentityToken string `json:"identity_token,omitempty"`
|
||||
// RegistryToken is a bearer token to be sent to a registry
|
||||
RegistryToken string `json:"registry_token,omitempty"`
|
||||
type ContainerPullImageInput struct {
|
||||
Image string `json:"image"`
|
||||
PullPolicy apis.ImagePullPolicy `json:"pull_policy"`
|
||||
Auth *apis.ContainerPullImageAuthConfig `json:"auth"`
|
||||
}
|
||||
|
||||
type ContainerPullImageInput struct {
|
||||
Image string `json:"image"`
|
||||
PullPolicy apis.ImagePullPolicy `json:"pull_policy"`
|
||||
Auth *ContainerPullImageAuthConfig `json:"auth"`
|
||||
type ContainerPushImageInput struct {
|
||||
Image string `json:"image"`
|
||||
Auth *apis.ContainerPullImageAuthConfig `json:"auth"`
|
||||
}
|
||||
|
||||
type ContainerDesc struct {
|
||||
@@ -121,3 +114,8 @@ type ContainerSaveVolumeMountToImageInput struct {
|
||||
VolumeMountIndex int `json:"volume_mount_index"`
|
||||
VolumeMount *ContainerVolumeMount `json:"volume_mount"`
|
||||
}
|
||||
|
||||
type ContainerCommitInput struct {
|
||||
Repository string `json:"repository"`
|
||||
Auth *apis.ContainerPullImageAuthConfig `json:"auth"`
|
||||
}
|
||||
|
||||
@@ -551,6 +551,10 @@ func (p *SPodDriver) RequestSaveVolumeMountImage(ctx context.Context, userCred m
|
||||
return p.performContainerAction(ctx, userCred, task, "save-volume-mount-to-image", task.GetParams())
|
||||
}
|
||||
|
||||
func (p *SPodDriver) RequestCommitContainer(ctx context.Context, userCred mcclient.TokenCredential, task models.IContainerTask) error {
|
||||
return p.performContainerAction(ctx, userCred, task, "commit", task.GetParams())
|
||||
}
|
||||
|
||||
func (p *SPodDriver) RequestDiskSnapshot(ctx context.Context, guest *models.SGuest, task taskman.ITask, snapshotId, diskId string) error {
|
||||
/*if guest.GetStatus() != api.VM_READY {
|
||||
return httperrors.NewNotAcceptableError("pod status %s is not ready", guest.GetStatus())
|
||||
|
||||
@@ -17,6 +17,9 @@ package models
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
@@ -29,10 +32,13 @@ import (
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
hostapi "yunion.io/x/onecloud/pkg/apis/host"
|
||||
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/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
kubemod "yunion.io/x/onecloud/pkg/mcclient/modules/k8s"
|
||||
)
|
||||
|
||||
var containerManager *SContainerManager
|
||||
@@ -757,3 +763,85 @@ func (c *SContainer) PerformStatus(ctx context.Context, userCred mcclient.TokenC
|
||||
}
|
||||
return c.SVirtualResourceBase.PerformStatus(ctx, userCred, query, input)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ type IPodDriver interface {
|
||||
RequestDeleteContainer(ctx context.Context, userCred mcclient.TokenCredential, task IContainerTask) error
|
||||
RequestSyncContainerStatus(ctx context.Context, userCred mcclient.TokenCredential, task IContainerTask) error
|
||||
RequestPullContainerImage(ctx context.Context, userCred mcclient.TokenCredential, task IContainerTask) error
|
||||
RequestCommitContainer(ctx context.Context, userCred mcclient.TokenCredential, task IContainerTask) error
|
||||
RequestSaveVolumeMountImage(ctx context.Context, userCred mcclient.TokenCredential, task IContainerTask) error
|
||||
RequestExecSyncContainer(ctx context.Context, userCred mcclient.TokenCredential, ctr *SContainer, input *compute.ContainerExecSyncInput) (jsonutils.JSONObject, error)
|
||||
RequestSetContainerResourcesLimit(ctx context.Context, cred mcclient.TokenCredential, c *SContainer, limit *apis.ContainerResources) (jsonutils.JSONObject, error)
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
// 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 tasks
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
)
|
||||
|
||||
func init() {
|
||||
taskman.RegisterTask(ContainerCommitTask{})
|
||||
}
|
||||
|
||||
type ContainerCommitTask struct {
|
||||
ContainerBaseTask
|
||||
}
|
||||
|
||||
func (t *ContainerCommitTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) {
|
||||
t.requestCommitImage(ctx, obj.(*models.SContainer))
|
||||
}
|
||||
|
||||
func (t *ContainerCommitTask) requestCommitImage(ctx context.Context, container *models.SContainer) {
|
||||
t.SetStage("OnCommitted", nil)
|
||||
if err := t.GetPodDriver().RequestCommitContainer(ctx, t.GetUserCred(), t); err != nil {
|
||||
t.OnCommittedFailed(ctx, container, jsonutils.NewString(err.Error()))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (t *ContainerCommitTask) OnCommitted(ctx context.Context, container *models.SContainer, data jsonutils.JSONObject) {
|
||||
t.SetStage("OnSyncStatus", nil)
|
||||
container.StartSyncStatusTask(ctx, t.GetUserCred(), t.GetTaskId())
|
||||
}
|
||||
|
||||
func (t *ContainerCommitTask) OnCommittedFailed(ctx context.Context, container *models.SContainer, reason jsonutils.JSONObject) {
|
||||
container.SetStatus(ctx, t.GetUserCred(), api.CONTAINER_STATUS_COMMIT_FAILED, reason.String())
|
||||
t.SetStageFailed(ctx, reason)
|
||||
}
|
||||
|
||||
func (t *ContainerCommitTask) OnSyncStatus(ctx context.Context, container *models.SContainer, data jsonutils.JSONObject) {
|
||||
t.SetStageComplete(ctx, nil)
|
||||
}
|
||||
func (t *ContainerCommitTask) OnSyncStatusFailed(ctx context.Context, container *models.SContainer, reason jsonutils.JSONObject) {
|
||||
t.SetStageFailed(ctx, reason)
|
||||
}
|
||||
+32
-22
@@ -57,8 +57,8 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/util/cgrouputils/cpuset"
|
||||
"yunion.io/x/onecloud/pkg/util/fileutils2"
|
||||
"yunion.io/x/onecloud/pkg/util/pod"
|
||||
"yunion.io/x/onecloud/pkg/util/pod/image"
|
||||
"yunion.io/x/onecloud/pkg/util/pod/logs"
|
||||
"yunion.io/x/onecloud/pkg/util/pod/nerdctl"
|
||||
"yunion.io/x/onecloud/pkg/util/procutils"
|
||||
)
|
||||
|
||||
@@ -117,6 +117,7 @@ type PodInstance interface {
|
||||
ExecContainer(ctx context.Context, userCred mcclient.TokenCredential, ctrId string, input *computeapi.ContainerExecInput) (*url.URL, error)
|
||||
ContainerExecSync(ctx context.Context, userCred mcclient.TokenCredential, ctrId string, input *computeapi.ContainerExecSyncInput) (jsonutils.JSONObject, error)
|
||||
SetContainerResourceLimit(ctrId string, limit *apis.ContainerResources) (jsonutils.JSONObject, error)
|
||||
CommitContainer(ctx context.Context, userCred mcclient.TokenCredential, ctrId string, input *hostapi.ContainerCommitInput) (jsonutils.JSONObject, error)
|
||||
|
||||
ReadLogs(ctx context.Context, userCred mcclient.TokenCredential, ctrId string, input *computeapi.PodLogOptions, stdout, stderr io.Writer) error
|
||||
}
|
||||
@@ -1624,27 +1625,8 @@ func (s *sPodGuestInstance) PullImage(ctx context.Context, userCred mcclient.Tok
|
||||
}
|
||||
|
||||
func (s *sPodGuestInstance) pullImageByCtrCmd(ctx context.Context, userCred mcclient.TokenCredential, ctrId string, input *hostapi.ContainerPullImageInput) (jsonutils.JSONObject, error) {
|
||||
opt := &image.PullOptions{
|
||||
SkipVerify: true,
|
||||
}
|
||||
if input.Auth != nil {
|
||||
opt.Username = input.Auth.Username
|
||||
opt.Password = input.Auth.Password
|
||||
}
|
||||
addr := options.HostOptions.ContainerRuntimeEndpoint
|
||||
addr = strings.TrimPrefix(addr, "unix://")
|
||||
imgTool := image.NewImageTool(addr, "k8s.io")
|
||||
output, err := imgTool.Pull(input.Image, opt)
|
||||
errs := make([]error, 0)
|
||||
if err != nil {
|
||||
// try http protocol
|
||||
errs = append(errs, errors.Wrapf(err, "pullImageByCtrCmd: %s", output))
|
||||
opt.PlainHttp = true
|
||||
log.Infof("try pull image %s by http", input.Image)
|
||||
if output2, err := imgTool.Pull(input.Image, opt); err != nil {
|
||||
errs = append(errs, errors.Wrapf(err, "pullImageByCtrCmd by http: %s", output2))
|
||||
return nil, errors.NewAggregate(errs)
|
||||
}
|
||||
if err := PullContainerdImage(input); err != nil {
|
||||
return nil, errors.Wrap(err, "pull containerd image")
|
||||
}
|
||||
return jsonutils.Marshal(&runtimeapi.PullImageResponse{
|
||||
ImageRef: input.Image,
|
||||
@@ -2003,3 +1985,31 @@ func (s *sPodGuestInstance) ReadLogs(ctx context.Context, userCred mcclient.Toke
|
||||
opts := logs.NewLogOptions(input, time.Now())
|
||||
return logs.ReadLogs(ctx, logPath, ctrCriId, opts, s.getCRI().GetRuntimeClient(), stdout, stderr)
|
||||
}
|
||||
|
||||
func (s *sPodGuestInstance) CommitContainer(ctx context.Context, userCred mcclient.TokenCredential, ctrId string, input *hostapi.ContainerCommitInput) (jsonutils.JSONObject, error) {
|
||||
criId, err := s.getContainerCRIId(ctrId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get container cri id")
|
||||
}
|
||||
|
||||
// 1. commit
|
||||
tool := NewContainerdNerdctl()
|
||||
imgRepo, err := tool.Commit(criId, &nerdctl.CommitOptions{Repository: input.Repository})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "commit container %s image", ctrId)
|
||||
}
|
||||
log.Infof("container %s was commited to %s", ctrId, imgRepo)
|
||||
|
||||
// 2. push to repository
|
||||
if err := PushContainerdImage(&hostapi.ContainerPushImageInput{
|
||||
Image: imgRepo,
|
||||
Auth: input.Auth,
|
||||
}); err != nil {
|
||||
return nil, errors.Wrapf(err, "push container %s image", ctrId)
|
||||
}
|
||||
log.Infof("container %s was pushed to %s", ctrId, imgRepo)
|
||||
|
||||
return jsonutils.Marshal(map[string]interface{}{
|
||||
"image_repository": imgRepo,
|
||||
}), nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
// 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 guestman
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
hostapi "yunion.io/x/onecloud/pkg/apis/host"
|
||||
"yunion.io/x/onecloud/pkg/hostman/options"
|
||||
"yunion.io/x/onecloud/pkg/util/pod/image"
|
||||
"yunion.io/x/onecloud/pkg/util/pod/nerdctl"
|
||||
)
|
||||
|
||||
func GetContainerdConnectionInfo() (string, string) {
|
||||
addr := options.HostOptions.ContainerRuntimeEndpoint
|
||||
addr = strings.TrimPrefix(addr, "unix://")
|
||||
namespace := "k8s.io"
|
||||
return addr, namespace
|
||||
}
|
||||
|
||||
func NewContainerdImageTool() image.ImageTool {
|
||||
addr, namespace := GetContainerdConnectionInfo()
|
||||
return image.NewImageTool(addr, namespace)
|
||||
}
|
||||
|
||||
func NewContainerdNerdctl() nerdctl.Nerdctl {
|
||||
addr, namespace := GetContainerdConnectionInfo()
|
||||
return nerdctl.NewNerdctl(addr, namespace)
|
||||
}
|
||||
|
||||
func PullContainerdImage(input *hostapi.ContainerPullImageInput) error {
|
||||
opt := &image.PullOptions{
|
||||
RepoCommonOptions: image.RepoCommonOptions{
|
||||
SkipVerify: true,
|
||||
},
|
||||
}
|
||||
if input.Auth != nil {
|
||||
opt.Username = input.Auth.Username
|
||||
opt.Password = input.Auth.Password
|
||||
}
|
||||
imgTool := NewContainerdImageTool()
|
||||
output, err := imgTool.Pull(input.Image, opt)
|
||||
errs := make([]error, 0)
|
||||
if err != nil {
|
||||
// try http protocol
|
||||
errs = append(errs, errors.Wrapf(err, "pullImageByCtrCmd: %s", output))
|
||||
opt.PlainHttp = true
|
||||
log.Infof("try pull image %s by http", input.Image)
|
||||
if output2, err := imgTool.Pull(input.Image, opt); err != nil {
|
||||
errs = append(errs, errors.Wrapf(err, "pullImageByCtrCmd by http: %s", output2))
|
||||
return errors.NewAggregate(errs)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func PushContainerdImage(input *hostapi.ContainerPushImageInput) error {
|
||||
opt := &image.PushOptions{
|
||||
RepoCommonOptions: image.RepoCommonOptions{
|
||||
SkipVerify: true,
|
||||
},
|
||||
}
|
||||
if input.Auth != nil {
|
||||
opt.Username = input.Auth.Username
|
||||
opt.Password = input.Auth.Password
|
||||
}
|
||||
imgTool := NewContainerdImageTool()
|
||||
err := imgTool.Push(input.Image, opt)
|
||||
errs := make([]error, 0)
|
||||
if err != nil {
|
||||
// try http protocol
|
||||
errs = append(errs, errors.Wrap(err, "pushImageByCtrCmd: %s"))
|
||||
opt.PlainHttp = true
|
||||
log.Infof("try push image %s by http", input.Image)
|
||||
if err := imgTool.Push(input.Image, opt); err != nil {
|
||||
errs = append(errs, errors.Wrapf(err, "pushImageByCtrCmd by http"))
|
||||
return errors.NewAggregate(errs)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -118,6 +118,7 @@ func AddPodHandlers(prefix string, app *appsrv.Application) {
|
||||
"sync-status": syncContainerStatus,
|
||||
"pull-image": pullImage,
|
||||
"save-volume-mount-to-image": saveVolumeMountToImage,
|
||||
"commit": commitContainer,
|
||||
}
|
||||
for action, f := range ctrHandlers {
|
||||
app.AddHandler("POST",
|
||||
@@ -309,3 +310,11 @@ func containerSetResourcesLimit(ctx context.Context, userCred mcclient.TokenCred
|
||||
}
|
||||
return pod.SetContainerResourceLimit(containerId, input)
|
||||
}
|
||||
|
||||
func commitContainer(ctx context.Context, userCred mcclient.TokenCredential, pod guestman.PodInstance, ctrId string, body jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
input := new(hostapi.ContainerCommitInput)
|
||||
if err := body.Unmarshal(input); err != nil {
|
||||
return nil, errors.Wrap(err, "unmarshal to ContainerCommitInput")
|
||||
}
|
||||
return pod.CommitContainer(ctx, userCred, ctrId, input)
|
||||
}
|
||||
|
||||
@@ -413,3 +413,34 @@ func (o *ContainerLogOptions) ToAPIInput() (*computeapi.PodLogOptions, error) {
|
||||
}
|
||||
return opt, nil
|
||||
}
|
||||
|
||||
type ContainerCommitOptions struct {
|
||||
ServerIdOptions
|
||||
RegistryId string `help:"Registry ID from kubeserver"`
|
||||
ImageName string `help:"Image name"`
|
||||
Tag string `help:"Tag"`
|
||||
ExternalRegistryUrl string `help:"External registry URL, e.g.: registry.cn-beijing.aliyuncs.com/yunionio"`
|
||||
ExternalRegistryUsername string `help:"External registry username"`
|
||||
ExternalRegistryPassword string `help:"External registry password"`
|
||||
}
|
||||
|
||||
func (o *ContainerCommitOptions) Params() (jsonutils.JSONObject, error) {
|
||||
input := &computeapi.ContainerCommitInput{
|
||||
RegistryId: o.RegistryId,
|
||||
ImageName: o.ImageName,
|
||||
Tag: o.Tag,
|
||||
ExternalRegistry: &computeapi.ContainerCommitExternalRegistry{
|
||||
Auth: &apis.ContainerPullImageAuthConfig{},
|
||||
},
|
||||
}
|
||||
if o.ExternalRegistryUrl != "" {
|
||||
input.ExternalRegistry.Url = o.ExternalRegistryUrl
|
||||
}
|
||||
if o.ExternalRegistryUsername != "" {
|
||||
input.ExternalRegistry.Auth.Username = o.ExternalRegistryUsername
|
||||
}
|
||||
if o.ExternalRegistryPassword != "" {
|
||||
input.ExternalRegistry.Auth.Password = o.ExternalRegistryPassword
|
||||
}
|
||||
return jsonutils.Marshal(input), nil
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
type ImageTool interface {
|
||||
Pull(image string, opt *PullOptions) (string, error)
|
||||
Push(image string, opt *PushOptions) error
|
||||
}
|
||||
|
||||
type imageTool struct {
|
||||
@@ -26,23 +27,26 @@ func NewImageTool(address, namespace string) ImageTool {
|
||||
|
||||
func (i imageTool) newCtrCmd(args ...string) *procutils.Command {
|
||||
reqArgs := []string{"--address", i.address}
|
||||
if i.namespace != "" {
|
||||
reqArgs = append(reqArgs, "--namespace", i.namespace)
|
||||
}
|
||||
args = append(reqArgs, args...)
|
||||
return procutils.NewRemoteCommandAsFarAsPossible("ctr", args...)
|
||||
}
|
||||
|
||||
type PullOptions struct {
|
||||
type RepoCommonOptions struct {
|
||||
SkipVerify bool
|
||||
PlainHttp bool
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
func (i imageTool) Pull(image string, opt *PullOptions) (string, error) {
|
||||
type PullOptions struct {
|
||||
RepoCommonOptions
|
||||
}
|
||||
|
||||
func (i imageTool) newRepoCommonArgs(opt RepoCommonOptions) []string {
|
||||
args := []string{}
|
||||
if i.namespace != "" {
|
||||
args = append(args, "--namespace", i.namespace)
|
||||
}
|
||||
args = append(args, []string{"images", "pull"}...)
|
||||
if opt.PlainHttp {
|
||||
args = append(args, "--plain-http")
|
||||
}
|
||||
@@ -52,7 +56,15 @@ func (i imageTool) Pull(image string, opt *PullOptions) (string, error) {
|
||||
if opt.Username != "" && opt.Password != "" {
|
||||
args = append(args, "--user", fmt.Sprintf("%s:%s", opt.Username, opt.Password))
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func (i imageTool) Pull(image string, opt *PullOptions) (string, error) {
|
||||
args := []string{}
|
||||
args = append(args, []string{"images", "pull"}...)
|
||||
args = append(args, i.newRepoCommonArgs(opt.RepoCommonOptions)...)
|
||||
args = append(args, []string{image}...)
|
||||
|
||||
cmd := i.newCtrCmd(args...)
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
@@ -60,3 +72,21 @@ func (i imageTool) Pull(image string, opt *PullOptions) (string, error) {
|
||||
}
|
||||
return image, nil
|
||||
}
|
||||
|
||||
type PushOptions struct {
|
||||
RepoCommonOptions
|
||||
}
|
||||
|
||||
func (i imageTool) Push(image string, opt *PushOptions) error {
|
||||
args := []string{}
|
||||
args = append(args, []string{"images", "push"}...)
|
||||
args = append(args, i.newRepoCommonArgs(opt.RepoCommonOptions)...)
|
||||
args = append(args, []string{image}...)
|
||||
|
||||
cmd := i.newCtrCmd(args...)
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "push %s: %s", image, out)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
package nerdctl // import "yunion.io/x/onecloud/pkg/util/pod/nerdctl"
|
||||
@@ -0,0 +1,62 @@
|
||||
// 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 nerdctl
|
||||
|
||||
import (
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/util/procutils"
|
||||
)
|
||||
|
||||
type Nerdctl interface {
|
||||
Commit(ctrId string, opt *CommitOptions) (string, error)
|
||||
}
|
||||
|
||||
type CommitOptions struct {
|
||||
Repository string
|
||||
}
|
||||
|
||||
type nerdctl struct {
|
||||
address string
|
||||
namespace string
|
||||
}
|
||||
|
||||
func NewNerdctl(address, namespace string) Nerdctl {
|
||||
return &nerdctl{
|
||||
address: address,
|
||||
namespace: namespace,
|
||||
}
|
||||
}
|
||||
|
||||
func (n nerdctl) newCmd(args ...string) *procutils.Command {
|
||||
newArgs := []string{"--address", n.address}
|
||||
if n.namespace != "" {
|
||||
newArgs = append(newArgs, "--namespace", n.namespace)
|
||||
}
|
||||
newArgs = append(newArgs, args...)
|
||||
return procutils.NewCommand("nerdctl", newArgs...)
|
||||
}
|
||||
|
||||
func (n nerdctl) Commit(ctrId string, opt *CommitOptions) (string, error) {
|
||||
if opt.Repository == "" {
|
||||
return "", errors.Wrap(errors.ErrEmpty, "repository")
|
||||
}
|
||||
cmd := n.newCmd("commit", ctrId, opt.Repository)
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "commit %s %s: %s", ctrId, opt.Repository, out)
|
||||
}
|
||||
return opt.Repository, nil
|
||||
}
|
||||
Reference in New Issue
Block a user