feat(region,host): commit container to image repository (#21284)

This commit is contained in:
Zexi Li
2024-09-24 10:02:44 +08:00
committed by GitHub
parent d252c8d682
commit d07594faa4
17 changed files with 496 additions and 44 deletions
+1 -1
View File
@@ -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"
+1 -1
View File
@@ -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"
+1
View File
@@ -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:"-"`
+46
View File
@@ -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"`
}
+12
View File
@@ -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
View File
@@ -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"`
}
+4
View File
@@ -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())
+88
View File
@@ -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)
}
+1
View File
@@ -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
View File
@@ -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
}
+96
View File
@@ -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
}
+36 -6
View File
@@ -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
}
+1
View File
@@ -0,0 +1 @@
package nerdctl // import "yunion.io/x/onecloud/pkg/util/pod/nerdctl"
+62
View File
@@ -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
}