import.go 3.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
// Copyright (c) Huawei Technologies Co., Ltd. 2020. All rights reserved.
// isula-build licensed under the Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//     http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
// PURPOSE.
// See the Mulan PSL v2 for more details.
// Author: Zekun Liu
// Create: 2020-07-16
// Description: This file is used for command import

package main

import (
	"bufio"
	"context"
	"fmt"
	"io"
	"os"

	dockerref "github.com/containers/image/v5/docker/reference"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	pb "isula.org/isula-build/api/services"
	"isula.org/isula-build/util"
)

const (
	bufferSize     = 32 * 1024
	maxTarballSize = 10 * 1024 * 1024 * 1024 // support tarball max size at most 10G
	importExample  = `isula-build ctr-img import busybox.tar busybox:isula`
	importArgsLen  = 1
)

type importOptions struct {
	source    string
	reference string
}

var importOpts importOptions

// NewImportCmd returns import command
func NewImportCmd() *cobra.Command {
	importCmd := &cobra.Command{
		Use:     "import FILE [REPOSITORY[:TAG]]",
		Short:   "Import the base image from a tarball to the image store",
		Example: importExample,
		RunE:    importCommand,
	}
	return importCmd
}

func importCommand(c *cobra.Command, args []string) error {
	if len(args) < importArgsLen {
		return errors.New("requires at least one argument")
	}
	if err := util.CheckFileSize(args[0], maxTarballSize); err != nil {
		return err
	}
	importOpts.source = args[0]
	if len(args) > importArgsLen {
		importOpts.reference = args[1]
	}

	ctx := context.TODO()
	cli, err := NewClient(ctx)
	if err != nil {
		return err
	}
	return runImport(ctx, cli)
}

func runImport(ctx context.Context, cli Cli) error {
	if importOpts.reference != "" {
		if _, err := dockerref.Parse(importOpts.reference); err != nil {
			return err
		}
	}
	file, err := os.Open(importOpts.source)
	if err != nil {
		return err
	}
	defer func() {
		if ferr := file.Close(); ferr != nil {
			logrus.Warnf("Close file %s failed", importOpts.source)
		}
	}()

	rpcCli, err := cli.Client().Import(ctx)
	if err != nil {
		return err
	}

	reader := bufio.NewReader(file)
	buf := make([]byte, bufferSize, bufferSize)
	var length int
	for {
		length, err = reader.Read(buf)
		if err != nil && err != io.EOF {
			return err
		}
		if length == 0 {
			break
		}
		if err = rpcCli.Send(&pb.ImportRequest{
			Data:      buf[0:length],
			Reference: importOpts.reference,
		}); err != nil {
			return err
		}
	}

	resp, err := rpcCli.CloseAndRecv()
	if err != nil {
		return err
	}
	if resp == nil {
		return errors.New("import failed, got nil response")
	}
	fmt.Printf("Import success with image id: %s\n", resp.ImageID)
	return nil
}