275 lines
7.5 KiB
Diff
275 lines
7.5 KiB
Diff
|
|
From 5749a92be53a3e8a135b4f7e59e8fd6d470fbd55 Mon Sep 17 00:00:00 2001
|
||
|
|
From: DCCooper <1866858@gmail.com>
|
||
|
|
Date: Tue, 26 Oct 2021 14:20:07 +0800
|
||
|
|
Subject: [PATCH 05/16] cli:finish client load separated image
|
||
|
|
|
||
|
|
reason: support isula-build client side process info for load separated
|
||
|
|
image
|
||
|
|
ABI change:(client)
|
||
|
|
- --input: name of app images when load separated images
|
||
|
|
- --dir: path to separated images' tarball directory
|
||
|
|
- --base: base image tarball path of separated images
|
||
|
|
- --lib: lib image tarball path of separated images
|
||
|
|
- --no-check: skip sha256 check sum for legacy separated images loading
|
||
|
|
|
||
|
|
Signed-off-by: DCCooper <1866858@gmail.com>
|
||
|
|
---
|
||
|
|
cmd/cli/load.go | 113 ++++++++++++++++++++++++++++++++++++++++---
|
||
|
|
cmd/cli/load_test.go | 50 +++++++++++++++++--
|
||
|
|
cmd/cli/mock.go | 10 ++++
|
||
|
|
3 files changed, 160 insertions(+), 13 deletions(-)
|
||
|
|
|
||
|
|
diff --git a/cmd/cli/load.go b/cmd/cli/load.go
|
||
|
|
index 16e90a26..2a9df772 100644
|
||
|
|
--- a/cmd/cli/load.go
|
||
|
|
+++ b/cmd/cli/load.go
|
||
|
|
@@ -25,18 +25,32 @@ import (
|
||
|
|
"github.com/pkg/errors"
|
||
|
|
"github.com/spf13/cobra"
|
||
|
|
|
||
|
|
+ constant "isula.org/isula-build"
|
||
|
|
pb "isula.org/isula-build/api/services"
|
||
|
|
"isula.org/isula-build/util"
|
||
|
|
)
|
||
|
|
|
||
|
|
+type separatorLoadOption struct {
|
||
|
|
+ app string
|
||
|
|
+ base string
|
||
|
|
+ lib string
|
||
|
|
+ dir string
|
||
|
|
+ skipCheck bool
|
||
|
|
+ enabled bool
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
type loadOptions struct {
|
||
|
|
- path string
|
||
|
|
+ path string
|
||
|
|
+ loadID string
|
||
|
|
+ sep separatorLoadOption
|
||
|
|
}
|
||
|
|
|
||
|
|
var loadOpts loadOptions
|
||
|
|
|
||
|
|
const (
|
||
|
|
- loadExample = `isula-build ctr-img load -i busybox.tar`
|
||
|
|
+ loadExample = `isula-build ctr-img load -i busybox.tar
|
||
|
|
+isula-build ctr-img load -i app:latest -d /home/Images
|
||
|
|
+isula-build ctr-img load -i app:latest -d /home/Images -b /home/Images/base.tar.gz -l /home/Images/lib.tar.gz`
|
||
|
|
)
|
||
|
|
|
||
|
|
// NewLoadCmd returns image load command
|
||
|
|
@@ -49,12 +63,20 @@ func NewLoadCmd() *cobra.Command {
|
||
|
|
RunE: loadCommand,
|
||
|
|
}
|
||
|
|
|
||
|
|
- loadCmd.PersistentFlags().StringVarP(&loadOpts.path, "input", "i", "", "Path to local tarball")
|
||
|
|
+ loadCmd.PersistentFlags().StringVarP(&loadOpts.path, "input", "i", "", "Path to local tarball(or app image name when load separated images)")
|
||
|
|
+ loadCmd.PersistentFlags().StringVarP(&loadOpts.sep.dir, "dir", "d", "", "Path to separated image tarballs directory")
|
||
|
|
+ loadCmd.PersistentFlags().StringVarP(&loadOpts.sep.base, "base", "b", "", "Base image tarball path of separated images")
|
||
|
|
+ loadCmd.PersistentFlags().StringVarP(&loadOpts.sep.lib, "lib", "l", "", "Library image tarball path of separated images")
|
||
|
|
+ loadCmd.PersistentFlags().BoolVarP(&loadOpts.sep.skipCheck, "no-check", "", false, "Skip sha256 check sum for legacy separated images loading")
|
||
|
|
|
||
|
|
return loadCmd
|
||
|
|
}
|
||
|
|
|
||
|
|
func loadCommand(cmd *cobra.Command, args []string) error {
|
||
|
|
+ if err := loadOpts.checkLoadOpts(); err != nil {
|
||
|
|
+ return errors.Wrapf(err, "check load options failed")
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
ctx := context.Background()
|
||
|
|
cli, err := NewClient(ctx)
|
||
|
|
if err != nil {
|
||
|
|
@@ -65,14 +87,20 @@ func loadCommand(cmd *cobra.Command, args []string) error {
|
||
|
|
}
|
||
|
|
|
||
|
|
func runLoad(ctx context.Context, cli Cli) error {
|
||
|
|
- var err error
|
||
|
|
-
|
||
|
|
- if loadOpts.path, err = resolveLoadPath(loadOpts.path); err != nil {
|
||
|
|
- return err
|
||
|
|
+ loadOpts.loadID = util.GenerateNonCryptoID()[:constant.DefaultIDLen]
|
||
|
|
+ sep := &pb.SeparatorLoad{
|
||
|
|
+ App: loadOpts.sep.app,
|
||
|
|
+ Dir: loadOpts.sep.dir,
|
||
|
|
+ Base: loadOpts.sep.base,
|
||
|
|
+ Lib: loadOpts.sep.lib,
|
||
|
|
+ SkipCheck: loadOpts.sep.skipCheck,
|
||
|
|
+ Enabled: loadOpts.sep.enabled,
|
||
|
|
}
|
||
|
|
|
||
|
|
resp, err := cli.Client().Load(ctx, &pb.LoadRequest{
|
||
|
|
- Path: loadOpts.path,
|
||
|
|
+ Path: loadOpts.path,
|
||
|
|
+ LoadID: loadOpts.loadID,
|
||
|
|
+ Sep: sep,
|
||
|
|
})
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
@@ -114,3 +142,72 @@ func resolveLoadPath(path string) (string, error) {
|
||
|
|
|
||
|
|
return path, nil
|
||
|
|
}
|
||
|
|
+
|
||
|
|
+func (opt *loadOptions) checkLoadOpts() error {
|
||
|
|
+ // normal load
|
||
|
|
+ if !opt.sep.isEnabled() {
|
||
|
|
+ path, err := resolveLoadPath(opt.path)
|
||
|
|
+ if err != nil {
|
||
|
|
+ return err
|
||
|
|
+ }
|
||
|
|
+ opt.path = path
|
||
|
|
+
|
||
|
|
+ return nil
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ // load separated image
|
||
|
|
+ opt.sep.enabled = true
|
||
|
|
+ if len(opt.path) == 0 {
|
||
|
|
+ return errors.New("app image should not be empty")
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ // Use opt.path as app image name when operating separated images
|
||
|
|
+ // this can be mark as a switch for handling separated images
|
||
|
|
+ opt.sep.app = opt.path
|
||
|
|
+
|
||
|
|
+ if err := opt.sep.check(); err != nil {
|
||
|
|
+ return err
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ return nil
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+func (sep *separatorLoadOption) isEnabled() bool {
|
||
|
|
+ return util.AnyFlagSet(sep.dir, sep.base, sep.lib, sep.app)
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+func (sep *separatorLoadOption) check() error {
|
||
|
|
+ pwd, err := os.Getwd()
|
||
|
|
+ if err != nil {
|
||
|
|
+ return errors.New("get current path failed")
|
||
|
|
+ }
|
||
|
|
+ if !util.IsValidImageName(sep.app) {
|
||
|
|
+ return errors.Errorf("invalid image name: %s", sep.app)
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ if len(sep.base) != 0 {
|
||
|
|
+ path, err := resolveLoadPath(sep.base)
|
||
|
|
+ if err != nil {
|
||
|
|
+ return errors.Wrap(err, "resolve base tarball path failed")
|
||
|
|
+ }
|
||
|
|
+ sep.base = path
|
||
|
|
+ }
|
||
|
|
+ if len(sep.lib) != 0 {
|
||
|
|
+ path, err := resolveLoadPath(sep.lib)
|
||
|
|
+ if err != nil {
|
||
|
|
+ return errors.Wrap(err, "resolve lib tarball path failed")
|
||
|
|
+ }
|
||
|
|
+ sep.lib = path
|
||
|
|
+ }
|
||
|
|
+ if len(sep.dir) == 0 {
|
||
|
|
+ return errors.New("image tarball directory should not be empty")
|
||
|
|
+ }
|
||
|
|
+ if !filepath.IsAbs(sep.dir) {
|
||
|
|
+ sep.dir = util.MakeAbsolute(sep.dir, pwd)
|
||
|
|
+ }
|
||
|
|
+ if !util.IsExist(sep.dir) {
|
||
|
|
+ return errors.Errorf("image tarball directory %s is not exist", sep.dir)
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ return nil
|
||
|
|
+}
|
||
|
|
diff --git a/cmd/cli/load_test.go b/cmd/cli/load_test.go
|
||
|
|
index 9c753e23..b7bf2a57 100644
|
||
|
|
--- a/cmd/cli/load_test.go
|
||
|
|
+++ b/cmd/cli/load_test.go
|
||
|
|
@@ -15,19 +15,59 @@ package main
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
+ "io/ioutil"
|
||
|
|
"path/filepath"
|
||
|
|
"testing"
|
||
|
|
|
||
|
|
"gotest.tools/v3/assert"
|
||
|
|
"gotest.tools/v3/fs"
|
||
|
|
+ constant "isula.org/isula-build"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestLoadCmd(t *testing.T) {
|
||
|
|
- cmd := NewLoadCmd()
|
||
|
|
- err := cmd.Execute()
|
||
|
|
- assert.Equal(t, err != nil, true)
|
||
|
|
- err = loadCommand(cmd, nil)
|
||
|
|
- assert.ErrorContains(t, err, "isula_build")
|
||
|
|
+ tmpDir := fs.NewFile(t, t.Name())
|
||
|
|
+ err := ioutil.WriteFile(tmpDir.Path(), []byte("This is test file"), constant.DefaultSharedFileMode)
|
||
|
|
+ assert.NilError(t, err)
|
||
|
|
+ defer tmpDir.Remove()
|
||
|
|
+
|
||
|
|
+ type testcase struct {
|
||
|
|
+ name string
|
||
|
|
+ path string
|
||
|
|
+ errString string
|
||
|
|
+ args []string
|
||
|
|
+ wantErr bool
|
||
|
|
+ sep separatorLoadOption
|
||
|
|
+ }
|
||
|
|
+ // For normal cases, default err is "invalid socket path: unix:///var/run/isula_build.sock".
|
||
|
|
+ // As daemon is not running as we run unit test.
|
||
|
|
+ var testcases = []testcase{
|
||
|
|
+ {
|
||
|
|
+ name: "TC1 - normal case",
|
||
|
|
+ path: tmpDir.Path(),
|
||
|
|
+ errString: "isula_build.sock",
|
||
|
|
+ wantErr: true,
|
||
|
|
+ },
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
+ for _, tc := range testcases {
|
||
|
|
+ t.Run(tc.name, func(t *testing.T) {
|
||
|
|
+ loadCmd := NewLoadCmd()
|
||
|
|
+ loadOpts = loadOptions{
|
||
|
|
+ path: tc.path,
|
||
|
|
+ sep: tc.sep,
|
||
|
|
+ }
|
||
|
|
+ err := loadCmd.Execute()
|
||
|
|
+ assert.Equal(t, err != nil, true)
|
||
|
|
+
|
||
|
|
+ err = loadCommand(loadCmd, tc.args)
|
||
|
|
+ if tc.wantErr {
|
||
|
|
+ assert.ErrorContains(t, err, tc.errString)
|
||
|
|
+ }
|
||
|
|
+ if !tc.wantErr {
|
||
|
|
+ assert.NilError(t, err)
|
||
|
|
+ }
|
||
|
|
+ })
|
||
|
|
+ }
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestRunLoad(t *testing.T) {
|
||
|
|
diff --git a/cmd/cli/mock.go b/cmd/cli/mock.go
|
||
|
|
index 2ae07d56..142c87fa 100644
|
||
|
|
--- a/cmd/cli/mock.go
|
||
|
|
+++ b/cmd/cli/mock.go
|
||
|
|
@@ -318,6 +318,16 @@ func (f *mockDaemon) importImage(_ context.Context, opts ...grpc.CallOption) (pb
|
||
|
|
|
||
|
|
func (f *mockDaemon) load(_ context.Context, in *pb.LoadRequest, opts ...grpc.CallOption) (pb.Control_LoadClient, error) {
|
||
|
|
f.loadReq = in
|
||
|
|
+ path := f.loadReq.Path
|
||
|
|
+ sep := f.loadReq.Sep
|
||
|
|
+ if !sep.Enabled {
|
||
|
|
+ if path == "" {
|
||
|
|
+ return &mockLoadClient{}, errors.Errorf("tarball path should not be empty")
|
||
|
|
+ }
|
||
|
|
+ _, err := resolveLoadPath(path)
|
||
|
|
+ return &mockLoadClient{}, err
|
||
|
|
+ }
|
||
|
|
+
|
||
|
|
return &mockLoadClient{}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
--
|
||
|
|
2.27.0
|
||
|
|
|