提交 eb109819 编写于 作者: O openeuler-ci-bot 提交者: Gitee

!18 isula-build: add info command to get isula-build system information

Merge pull request !18 from Vanient/master
此差异已折叠。
......@@ -24,7 +24,7 @@ service Control {
rpc Status(StatusRequest) returns (stream StatusResponse);
// List lists all images in isula-builder
rpc List(ListRequest) returns (ListResponse);
// Version requests the version information of isula-builder
// Version requests version information of isula-builder
rpc Version(google.protobuf.Empty) returns (VersionResponse);
// Remove sends an image remove request to isula-builder
rpc Remove(RemoveRequest) returns (stream RemoveResponse);
......@@ -39,9 +39,11 @@ service Control {
// Import requests import a new image
rpc Import(stream ImportRequest) returns (ImportResponse);
// Tag requests to tag an image
rpc tag(TagRequest) returns (google.protobuf.Empty);
rpc Tag(TagRequest) returns (google.protobuf.Empty);
// Save saves the image to tarball
rpc Save(SaveRequest) returns (stream SaveResponse);
// Info requests isula-build system information
rpc Info(google.protobuf.Empty) returns (InfoResponse);
}
message BuildRequest {
......@@ -219,3 +221,49 @@ message SaveResponse {
// log is log send to cli
string log = 2;
}
message MemData {
// memTotal is total memory
int64 memTotal = 1;
// memFree is free memory
int64 memFree = 2;
// swapTotal is total swap
int64 swapTotal = 3;
// swapFree is free swap
int64 swapFree = 4;
}
message StorageData {
// storageDriver is storage driver for isula-build
string storageDriver = 1;
// storageBackingFs is storage backing file system
string storageBackingFs = 2;
}
message RegistryData {
// registriesSearch is registries.search registries
repeated string registriesSearch = 1;
// registriesInsecure is registries.insecure registries
repeated string registriesInsecure = 2;
// registriesBlock is registries.block registries
repeated string registriesBlock = 3;
}
message InfoResponse {
// memInfo is memory information
MemData memInfo = 1;
// storageInfo is storage information
StorageData storageInfo = 2;
// registryInfo is registry information
RegistryData registryInfo = 3;
// dataRoot is persistent directory for isula-build
string dataRoot = 4;
// runRoot is runtime directory for isula-build
string runRoot = 5;
// ociRuntime is OCI runtime for RUN command
string OCIRuntime = 6;
// builderNum is number of builders
int64 builderNum = 7;
// goRoutines is number of go routines
int64 goRoutines = 8;
}
......@@ -94,6 +94,10 @@ func (gcli *mockGrpcClient) Version(ctx context.Context, in *types.Empty, opts .
}, nil
}
func (gcli *mockGrpcClient) Info(ctx context.Context, in *types.Empty, opts ...grpc.CallOption) (*pb.InfoResponse, error) {
return &pb.InfoResponse{}, nil
}
func (gcli *mockGrpcClient) Tag(ctx context.Context, in *pb.TagRequest, opts ...grpc.CallOption) (*types.Empty, error) {
return &types.Empty{}, nil
}
......
// 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: Danni Xia
// Create: 2020-08-03
// Description: This file is used for info command
package main
import (
"context"
"fmt"
"github.com/gogo/protobuf/types"
"github.com/spf13/cobra"
pb "isula.org/isula-build/api/services"
"isula.org/isula-build/util"
)
const (
infoExample = `isula-build info`
)
type infoOptions struct {
humanReadable bool
}
var infoOpts infoOptions
// NewInfoCmd returns info command
func NewInfoCmd() *cobra.Command {
// infoCmd represents the "info" command
infoCmd := &cobra.Command{
Use: "info",
Short: "Show isula-build system information",
RunE: infoCommand,
Args: util.NoArgs,
Example: infoExample,
}
infoCmd.PersistentFlags().BoolVarP(&infoOpts.humanReadable, "human-readable", "H", false,
"print memory info in human readable format, use powers of 1000")
return infoCmd
}
func infoCommand(c *cobra.Command, args []string) error {
ctx := context.Background()
cli, err := NewClient(ctx)
if err != nil {
return err
}
infoData, err := getInfo(ctx, cli)
if err != nil {
return err
}
printInfo(infoData)
return nil
}
func getInfo(ctx context.Context, cli Cli) (*pb.InfoResponse, error) {
return cli.Client().Info(ctx, &types.Empty{})
}
func printInfo(infoData *pb.InfoResponse) {
fmt.Println("General:")
if infoOpts.humanReadable {
fmt.Println(" MemTotal: ", util.FormatSize(float64(infoData.MemInfo.MemTotal)))
fmt.Println(" MemFree: ", util.FormatSize(float64(infoData.MemInfo.MemFree)))
fmt.Println(" SwapTotal: ", util.FormatSize(float64(infoData.MemInfo.SwapTotal)))
fmt.Println(" SwapFree: ", util.FormatSize(float64(infoData.MemInfo.SwapFree)))
} else {
fmt.Println(" MemTotal: ", infoData.MemInfo.MemTotal)
fmt.Println(" MemFree: ", infoData.MemInfo.MemFree)
fmt.Println(" SwapTotal: ", infoData.MemInfo.SwapTotal)
fmt.Println(" SwapFree: ", infoData.MemInfo.SwapFree)
}
fmt.Println(" OCI Runtime: ", infoData.OCIRuntime)
fmt.Println(" DataRoot: ", infoData.DataRoot)
fmt.Println(" RunRoot: ", infoData.RunRoot)
fmt.Println(" Builders: ", infoData.BuilderNum)
fmt.Println(" Goroutines: ", infoData.GoRoutines)
fmt.Println("Store:")
fmt.Println(" Storage Driver: ", infoData.StorageInfo.StorageDriver)
fmt.Println(" Backing Filesystem:", infoData.StorageInfo.StorageBackingFs)
fmt.Println("Registry:")
fmt.Println(" Search Registries:")
for _, registry := range infoData.RegistryInfo.RegistriesSearch {
fmt.Println(" ", registry)
}
fmt.Println(" Insecure Registries:")
for _, registry := range infoData.RegistryInfo.RegistriesInsecure {
fmt.Println(" ", registry)
}
}
// 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: Danni Xia
// Create: 2020-08-03
// Description: This file is used for testing command info
package main
import (
"context"
"testing"
"gotest.tools/assert"
pb "isula.org/isula-build/api/services"
)
func TestInfoCommand(t *testing.T) {
infoCmd := NewInfoCmd()
var args []string
err := infoCommand(infoCmd, args)
assert.ErrorContains(t, err, "isula_build.sock")
}
func TestGetInfoFromDaemon(t *testing.T) {
ctx := context.Background()
cli := newMockClient(&mockGrpcClient{})
_, err := getInfo(ctx, &cli)
assert.NilError(t, err)
}
func TestPrintInfo(t *testing.T) {
infoData := &pb.InfoResponse{
MemInfo: &pb.MemData{
MemTotal: 123,
MemFree: 123,
SwapTotal: 123,
SwapFree: 123,
},
StorageInfo: &pb.StorageData{
StorageDriver: "overlay",
StorageBackingFs: "extfs",
},
RegistryInfo: &pb.RegistryData{
RegistriesSearch: []string{"docker.io"},
RegistriesInsecure: []string{"localhost:5000"},
RegistriesBlock: nil,
},
DataRoot: "/var/lib/isula-build/",
RunRoot: "/var/run/isula-build/",
OCIRuntime: "runc",
BuilderNum: 0,
GoRoutines: 10,
}
printInfo(infoData)
}
......@@ -91,7 +91,7 @@ func main() {
func setupRootCmd(rootCmd *cobra.Command) {
rootCmd.SetFlagErrorFunc(util.FlagErrorFunc)
rootCmd.PersistentFlags().StringVar(&cliOpts.LogLevel, "log-level", "info", "Log level to be used. Either \"debug\", \"info\", \"warn\" or \"error\"")
rootCmd.PersistentFlags().StringVar(&cliOpts.LogLevel, "log-level", "error", "Log level to be used. Either \"debug\", \"info\", \"warn\" or \"error\"")
rootCmd.PersistentFlags().BoolVarP(&cliOpts.Debug, "debug", "D", false, "Open debug mode")
rootCmd.PersistentFlags().StringVarP(&cliOpts.Timeout, "timeout", "t", "500ms", "Timeout for connecting to daemon")
rootCmd.PersistentFlags().BoolP("help", "h", false, "Print usage")
......@@ -104,5 +104,6 @@ func addCommands(cmd *cobra.Command) {
NewVersionCmd(),
NewLoginCmd(),
NewLogoutCmd(),
NewInfoCmd(),
)
}
......@@ -15,7 +15,6 @@ package daemon
import (
"context"
"fmt"
"sort"
"strings"
......@@ -25,11 +24,11 @@ import (
constant "isula.org/isula-build"
pb "isula.org/isula-build/api/services"
"isula.org/isula-build/store"
"isula.org/isula-build/util"
)
const (
none = "<none>"
decimalPrefixBase = 1000
none = "<none>"
)
// List lists all images
......@@ -90,15 +89,5 @@ func getImageSize(store *store.Store, id string) string {
if err != nil {
imgSize = -1
}
return formatImageSize(float64(imgSize))
}
func formatImageSize(size float64) string {
suffixes := [5]string{"B", "KB", "MB", "GB", "TB"}
cnt := 0
for size >= decimalPrefixBase && cnt < len(suffixes)-1 {
size /= decimalPrefixBase
cnt++
}
return fmt.Sprintf("%.3g %s", size, suffixes[cnt])
return util.FormatSize(float64(imgSize))
}
// 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-22
// Description: This file is "import" command for backend
package daemon
import (
"io"
"strings"
cp "github.com/containers/image/v5/copy"
dockerref "github.com/containers/image/v5/docker/reference"
is "github.com/containers/image/v5/storage"
"github.com/containers/image/v5/tarball"
"github.com/containers/image/v5/transports"
"github.com/containers/storage/pkg/stringid"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
pb "isula.org/isula-build/api/services"
"isula.org/isula-build/builder/dockerfile"
"isula.org/isula-build/util"
)
const (
tmpFilePattern = "isula-build-ctr-img-import"
noneReference = "<none>:<none>"
bufLen = 1024
)
// Import an image from a tarball
func (b *Backend) Import(serv pb.Control_ImportServer) error {
localStore := b.daemon.localStore
buf := make([]byte, 0, bufLen)
reference := ""
for {
msg, ierr := serv.Recv()
if ierr == io.EOF {
break
}
if ierr != nil {
return ierr
}
if msg == nil {
return errors.New("import failed, receive nil msg")
}
reference = msg.Reference
buf = append(buf, msg.Data...)
}
logrus.Infof("Received and import image %q", reference)
reference, err := parseReference(reference)
if err != nil {
return err
}
srcRef, err := tarball.NewReference([]string{"-"}, buf)
if err != nil {
return err
}
tmpName := stringid.GenerateRandomID() + "-import-tmp"
dstRef, err := is.Transport.ParseStoreReference(localStore, tmpName)
if err != nil {
return err
}
policyContext, err := dockerfile.GetPolicyContext()
if err != nil {
return err
}
if _, err = cp.Image(serv.Context(), policyContext, dstRef, srcRef, nil); err != nil {
return err
}
img, err := is.Transport.GetStoreImage(localStore, dstRef)
if err != nil {
return errors.Wrapf(err, "error locating image %q in local storage after import", transports.ImageName(dstRef))
}
img.Names = append(img.Names, reference)
newNames := util.CopyStringsWithoutSpecificElem(img.Names, tmpName)
if err = localStore.SetNames(img.ID, newNames); err != nil {
return errors.Wrapf(err, "failed to prune temporary name from image %q", img.ID)
}
resp := &pb.ImportResponse{ImageID: img.ID}
if err = serv.SendAndClose(resp); err != nil {
return err
}
logrus.Infof("Import success with image id %q", img.ID)
return nil
}
func parseReference(ref string) (string, error) {
ref = strings.TrimSpace(ref)
if ref == "" {
return noneReference, nil
}
if _, err := dockerref.Parse(ref); err != nil {
return "", err
}
return ref, nil
}
// 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-22
// Description: This file is "import" command for backend
package daemon
import (
"io"
"strings"
cp "github.com/containers/image/v5/copy"
dockerref "github.com/containers/image/v5/docker/reference"
is "github.com/containers/image/v5/storage"
"github.com/containers/image/v5/tarball"
"github.com/containers/image/v5/transports"
"github.com/containers/storage/pkg/stringid"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
pb "isula.org/isula-build/api/services"
"isula.org/isula-build/builder/dockerfile"
"isula.org/isula-build/util"
)
const (
noneReference = "<none>:<none>"
bufLen = 1024
)
// Import an image from a tarball
func (b *Backend) Import(serv pb.Control_ImportServer) error {
logrus.Info("ImportRequest received")
localStore := b.daemon.localStore
buf := make([]byte, 0, bufLen)
reference := ""
for {
msg, ierr := serv.Recv()
if ierr == io.EOF {
break
}
if ierr != nil {
return ierr
}
if msg == nil {
return errors.New("import failed, receive nil msg")
}
reference = msg.Reference
buf = append(buf, msg.Data...)
}
logrus.Infof("Received and import image %q", reference)
reference, err := parseReference(reference)
if err != nil {
return err
}
srcRef, err := tarball.NewReference([]string{"-"}, buf)
if err != nil {
return err
}
tmpName := stringid.GenerateRandomID() + "-import-tmp"
dstRef, err := is.Transport.ParseStoreReference(localStore, tmpName)
if err != nil {
return err
}
policyContext, err := dockerfile.GetPolicyContext()
if err != nil {
return err
}
if _, err = cp.Image(serv.Context(), policyContext, dstRef, srcRef, nil); err != nil {
return err
}
img, err := is.Transport.GetStoreImage(localStore, dstRef)
if err != nil {
return errors.Wrapf(err, "error locating image %q in local storage after import", transports.ImageName(dstRef))
}
img.Names = append(img.Names, reference)
newNames := util.CopyStringsWithoutSpecificElem(img.Names, tmpName)
if err = localStore.SetNames(img.ID, newNames); err != nil {
return errors.Wrapf(err, "failed to prune temporary name from image %q", img.ID)
}
resp := &pb.ImportResponse{ImageID: img.ID}
if err = serv.SendAndClose(resp); err != nil {
return err
}
logrus.Infof("Import success with image id %q", img.ID)
return nil
}
func parseReference(ref string) (string, error) {
ref = strings.TrimSpace(ref)
if ref == "" {
return noneReference, nil
}
if _, err := dockerref.Parse(ref); err != nil {
return "", err
}
return ref, nil
}
// 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: Danni Xia
// Create: 2020-08-03
// Description: This file is "info" command for backend
package daemon
import (
"context"
"runtime"
"github.com/containers/image/v5/pkg/sysregistriesv2"
"github.com/containers/storage/pkg/system"
gogotypes "github.com/gogo/protobuf/types"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
pb "isula.org/isula-build/api/services"
"isula.org/isula-build/image"
)
// Info to get isula-build system information
func (b *Backend) Info(ctx context.Context, req *gogotypes.Empty) (*pb.InfoResponse, error) {
logrus.Info("InfoRequest received")
// get memory information
memInfo, err := system.ReadMemInfo()
if err != nil {
return nil, errors.Wrapf(err, "get memory information err")
}
// get storage backing file system information
var storageBackingFs string
status, err := b.daemon.localStore.Status()
if err != nil {
return nil, errors.Wrapf(err, "get storage status info err")
}
for _, pair := range status {
if pair[0] == "Backing Filesystem" {
storageBackingFs = pair[1]
}
}
// get registry information
registriesSearch, registriesInsecure, registriesBlock, err := getRegistryInfo()
if err != nil {
return nil, errors.Wrapf(err, "get registries info err")
}
// generate info response
infoResponse := &pb.InfoResponse{
MemInfo: &pb.MemData{
MemTotal: memInfo.MemTotal,
MemFree: memInfo.MemFree,
SwapTotal: memInfo.SwapTotal,
SwapFree: memInfo.SwapFree,
},
StorageInfo: &pb.StorageData{
StorageDriver: b.daemon.localStore.GraphDriverName(),
StorageBackingFs: storageBackingFs,
},
RegistryInfo: &pb.RegistryData{
RegistriesSearch: registriesSearch,
RegistriesInsecure: registriesInsecure,
RegistriesBlock: registriesBlock,
},
DataRoot: b.daemon.opts.DataRoot,
RunRoot: b.daemon.opts.RunRoot,
OCIRuntime: b.daemon.opts.RuntimePath,
BuilderNum: int64(len(b.daemon.builders)),
GoRoutines: int64(runtime.NumGoroutine()),
}
// default OCI runtime is "runc"
if infoResponse.OCIRuntime == "" {
infoResponse.OCIRuntime = "runc"
}
return infoResponse, nil
}
func getRegistryInfo() ([]string, []string, []string, error) {
registriesInsecure := make([]string, 0, 0)
registriesBlock := make([]string, 0, 0)
systemContext := image.GetSystemContext()
registriesSearch, err := sysregistriesv2.UnqualifiedSearchRegistries(systemContext)
if err != nil {
return nil, nil, nil, err
}
registries, err := sysregistriesv2.GetRegistries(systemContext)
if err != nil {
return nil, nil, nil, err
}
for _, registry := range registries {
if registry.Insecure {
registriesInsecure = append(registriesInsecure, registry.Prefix)
}
if registry.Blocked {
registriesBlock = append(registriesBlock, registry.Prefix)
}
}
return registriesSearch, registriesInsecure, registriesBlock, nil
}
......@@ -24,8 +24,10 @@
* [-a, --all](#-a---all-1)
* [Version query](#version-query)
* [Tag an image](#tag-an-image)
* [Show system information](#show-system-information)
<!-- vim-markdown-toc -->
# Usage
## Configuration
......@@ -293,6 +295,31 @@ Deleted: sha256:78731c1dde25361f539555edaf8f0b24132085b7cab6ecb90de63d72fa00c01d
Deleted: sha256:eeba1bfe9fca569a894d525ed291bdaef389d28a88c288914c1a9db7261ad12c
```
### Tag an image
We can use the `tag` command to add an additional tag to an image.
Usage:
`isula-build ctr-img tag <imageID>/<imageName> busybox:latest`
```bash
$ sudo isula-build ctr-img images
---------------------------------------------- ----------- ----------------- -------------------------- ------------
REPOSITORY TAG IMAGE ID CREATED SIZE
---------------------------------------------- ----------- ----------------- -------------------------- ------------
docker.io/library/alpine latest a24bb4013296 2020-05-29 21:19:46 5.85 MB
---------------------------------------------- ----------- ----------------- -------------------------- ------------
$ sudo isula-build ctr-img tag a24bb4013296 alpine:latest
$ sudo isula-build ctr-img images
---------------------------------------------- ----------- ----------------- -------------------------- ------------
REPOSITORY TAG IMAGE ID CREATED SIZE
---------------------------------------------- ----------- ----------------- -------------------------- ------------
docker.io/library/alpine latest a24bb4013296 2020-05-29 21:19:46 5.85 MB
localhost/alpine latest a24bb4013296 2020-05-29 21:19:46 5.85 MB
---------------------------------------------- ----------- ----------------- -------------------------- ------------
```
### Authentication of the Remote Image Repository
We can `login` or `logout` an image repository
......@@ -343,7 +370,7 @@ Removed authentications
### Version query
We can run the version command to view the current version information.
We can run the `version` command to view the current version information.
```bash
$ sudo isula-build version
......@@ -362,28 +389,43 @@ Server:
OS/Arch: linux/amd64
```
### Tag an image
### Show system information
We can use the `tag` command to add an additional tag to an image.
We can use the `info` command to view isula-build system information.
Currently, the following flags are supported:
```bash
Flags:
-H, --human-readable print memory info in human readable format, use powers of 1000
```
#### -H, --human-readable
print memory info in human readable format, use powers of 1000
Usage:
`isula-build ctr-img tag <imageID>/<imageName> busybox:latest`
`isula-build info -H`
```bash
$ sudo isula-build ctr-img images
---------------------------------------------- ----------- ----------------- -------------------------- ------------
REPOSITORY TAG IMAGE ID CREATED SIZE
---------------------------------------------- ----------- ----------------- -------------------------- ------------
docker.io/library/alpine latest a24bb4013296 2020-05-29 21:19:46 5.85 MB
---------------------------------------------- ----------- ----------------- -------------------------- ------------
$ sudo isula-build ctr-img tag a24bb4013296 alpine:latest
$ sudo isula-build ctr-img images
---------------------------------------------- ----------- ----------------- -------------------------- ------------
REPOSITORY TAG IMAGE ID CREATED SIZE
---------------------------------------------- ----------- ----------------- -------------------------- ------------
docker.io/library/alpine latest a24bb4013296 2020-05-29 21:19:46 5.85 MB
localhost/alpine latest a24bb4013296 2020-05-29 21:19:46 5.85 MB
---------------------------------------------- ----------- ----------------- -------------------------- ------------
$ sudo isula-build info -H
General:
MemTotal: 10.3 GB
MemFree: 2.24 GB
SwapTotal: 8.46 GB
SwapFree: 8.45 GB
OCI Runtime: runc
DataRoot: /var/lib/isula-build/
RunRoot: /var/run/isula-build/
Builders: 0
Goroutines: 12
Store:
Storage Driver: overlay
Backing Filesystem: extfs
Registry:
Search Registries:
docker.io
Insecure Registries:
localhost:5000
```
......@@ -15,6 +15,7 @@
package util
import (
"fmt"
"os"
"path/filepath"
"strings"
......@@ -29,6 +30,7 @@ import (
const (
maxServerNameLength = 255
maxLoadFileSize = 10 * 1024 * 1024 * 1024
decimalPrefixBase = 1000
)
// CopyMapStringString copies all KVs in a map[string]string to a new map
......@@ -150,3 +152,15 @@ func ParseServer(server string) (string, error) {
}
return fields[0], nil
}
// FormatSize formats size using powers of 1000
func FormatSize(size float64) string {
suffixes := [5]string{"B", "KB", "MB", "GB", "TB"}
cnt := 0
for size >= decimalPrefixBase && cnt < len(suffixes)-1 {
size /= decimalPrefixBase
cnt++
}
return fmt.Sprintf("%.3g %s", size, suffixes[cnt])
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册