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

!1 package init

Merge pull request !1 from wangkang101/master

要显示的变更太多。

To preserve performance only 1000 of 1000+ files are displayed.
NAME=isula-transform
VERSION=0.9.0
COMMIT=$(shell git rev-parse HEAD 2> /dev/null || true)
BEP_DIR=/tmp/isula-transform-build-bep
BEP_FLAGS=-tmpdir=$(BEP_DIR)
TAGS="cgo static_build"
LDFLAGS="-s -w -buildid=IdByiSula -buildmode=pie -extldflags=-zrelro -extldflags=-znow $(BEP_FLAGS) -X main.version=$(VERSION) -X main.gitCommit=${COMMIT}"
ENV=CGO_ENABLED=1
GOMOD_ENV=GO111MODULE=on
.PHONY: all
all: localbuild
.PHONY: bep
bep:
@mkdir -p $(BEP_DIR)
.PHONY: localbuild
localbuild: bep
@go mod vendor
$(ENV) $(GOMOD_ENV) go build -tags $(TAGS) -ldflags $(LDFLAGS) -mod=vendor -o bin/$(NAME) .
@rm -rf $(BEP_DIR)
.PHONY: bin
bin: bep
$(ENV) go build -tags $(TAGS) -ldflags $(LDFLAGS) -o bin/$(NAME) .
@rm -rf $(BEP_DIR)
.PHONY: install
install:
install -m 0750 ./bin/isula-transform /usr/bin/isula-transform
.PHONY: binclean
binclean:
@echo "clean built binary file"
@rm -rf ./bin/$(NAME)
.PHONY: mock
mock:
@echo "waiting for generate test mock file"
@go generate isula.org/isula-transform/...
.PHONY: mockclean
mockclean:
@echo "clean test mock file"
@find . -name "mock_*.go" -delete
.PHONY: runtest
runtest: mock
@echo "run test:"
go test -cover -timeout 30s isula.org/isula-transform/...
.PHONY: test
test: runtest mockclean
.PHONY: clean
clean: binclean mockclean
# isula-transform
#### Description
isula transform kit transform specify docker container to iSulad container
#### Software Architecture
Software architecture description
#### Installation
1. xxxx
2. xxxx
3. xxxx
#### Instructions
1. xxxx
2. xxxx
3. xxxx
#### Contribution
1. Fork the repository
2. Create Feat_xxx branch
3. Commit your code
4. Create Pull Request
#### Gitee Feature
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
4. The most valuable open source project [GVP](https://gitee.com/gvp)
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
# isula-transform
#### 介绍
isula transform kit transform specify docker container to iSulad container
## Description
#### 软件架构
软件架构说明
`isula-transform` is a tool which converts the configuration of the docker container to a type that isulad can recognize as being loaded.
## Build and Installation
#### 安装教程
Before building, ensure that [Golang](https://golang.org/) 1.13 or later and [lcr](https://gitee.com/openeuler/lcr) 2.0.1 or later are installed successfully.
1. xxxx
2. xxxx
3. xxxx
Just execute `sudo make && sudo make install` in the code root directory and enjoy it.
#### 使用说明
## Instructions
1. xxxx
2. xxxx
3. xxxx
Basic usage of isula-transform:
#### 参与贡献
``` txt
NAME:
isula-transform - transform specify docker container type configuration to iSulad type
1. Fork 本仓库
2. 新建 Feat_xxx 分支
3. 提交代码
4. 新建 Pull Request
USAGE:
[global options] --all|container_id[ container_id...]
COMMANDS:
help, h Shows a list of commands or help for one command
#### 码云特技
GLOBAL OPTIONS:
--log value specific output log file path (default: "/var/log/isula-kits/transform.log")
--log-level value Customize the level of logging for collection, allowed: debug, info, warn, error (default: "info")
--isulad-config-file value iSulad configuration file path (default: "/etc/isulad/daemon.json")
--docker-graph value graph root of docker (default: "/var/lib/docker")
--docker-state value state root of docker (default: "/var/run/docker")
--all transform all containers
--help, -h show help
--version, -v print the version
```
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
2. 码云官方博客 [blog.gitee.com](https://blog.gitee.com)
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解码云上的优秀开源项目
4. [GVP](https://gitee.com/gvp) 全称是码云最有价值开源项目,是码云综合评定出的优秀开源项目
5. 码云官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
6. 码云封面人物是一档用来展示码云会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
### NOTE
There are a few things to note about using `isula-transform` :
- currently, only docker 18.09 containers are supported to transform to isulad container
- due to isulad's lack of native network capability, docker container needs to configure host network
- `isula-transform` will read the container's OCI configuration, which requires the docker container to be in a pause or running state, and to be paused if it is in a running state
## Contributions
We always welcome new contributors. And we are happy to provide guidance for the new contributors.
## Licensing
`isula-transform` is licensed under the [Mulan PSL v2](https://license.coscl.org.cn/MulanPSL2/index.html).
此差异已折叠。
// Copyright (c) Huawei Technologies Co., Ltd. 2019-2020. All rights reserved.
// iSulad-img 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.
// Description: iSulad image kit
// Author: liuhao
// Create: 2019-07-12
// Since some of this code is derived from Kubernetes, their copyright
// is retained here....
//
// Copyright 2018 The Kubernetes Authors.
//
// 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.
// The original version of this proto can be found at
// https://github.com/cri-o/cri-o/blob/master/vendor/k8s.io/cri-api/pkg/apis/runtime/v1alpha2/api.proto
syntax = 'proto3';
package isula;
// ImageService defines the public APIs for managing images.
service ImageService {
// ListImages lists existing images.
rpc ListImages(ListImagesRequest) returns (ListImagesResponse) {}
// ImageStatus returns the status of the image. If the image is not
// present, returns a response with ImageStatusResponse.Image set to
// nil.
rpc ImageStatus(ImageStatusRequest) returns (ImageStatusResponse) {}
// Get image information
rpc ImageInfo(ImageInfoRequest) returns (ImageInfoResponse) {}
// PullImage pulls an image with authentication config.
rpc PullImage(PullImageRequest) returns (PullImageResponse) {}
// RemoveImage removes the image.
// This call is idempotent, and must not return an error if the image has
// already been removed.
rpc RemoveImage(RemoveImageRequest) returns (RemoveImageResponse) {}
// ImageFSInfo returns information of the filesystem that is used to store images.
rpc ImageFsInfo(ImageFsInfoRequest) returns (ImageFsInfoResponse) {}
// Load image from file
rpc LoadImage(LoadImageRequest) returns (LoadImageResponose) {}
// Import rootfs to be image
rpc Import(ImportRequest) returns (ImportResponose) {}
// isulad image services
// get all Container rootfs
rpc ListContainers(ListContainersRequest) returns (ListContainersResponse) {}
// create rootfs for container
rpc ContainerPrepare(ContainerPrepareRequest) returns (ContainerPrepareResponse) {}
// remove rootfs of container
rpc ContainerRemove(ContainerRemoveRequest) returns (ContainerRemoveResponse) {}
// mount rwlayer for container
rpc ContainerMount(ContainerMountRequest) returns (ContainerMountResponse) {}
// umount rwlayer of container
rpc ContainerUmount(ContainerUmountRequest) returns (ContainerUmountResponse) {}
// export container rootfs
rpc ContainerExport(ContainerExportRequest) returns (ContainerExportResponse) {}
// get filesystem usage of container
rpc ContainerFsUsage(ContainerFsUsageRequest) returns (ContainerFsUsageResponse) {}
// get status of graphdriver
rpc GraphdriverStatus(GraphdriverStatusRequest) returns (GraphdriverStatusResponse) {}
// get metadata of graphdriver
rpc GraphdriverMetadata(GraphdriverMetadataRequest) returns (GraphdriverMetadataResponse) {}
// login registry
rpc Login(LoginRequest) returns (LoginResponse) {}
// logout registry
rpc Logout(LogoutRequest) returns (LogoutResponse) {}
// health check service
rpc HealthCheck(HealthCheckRequest) returns (HealthCheckResponse) {}
// Add a tag to the image
rpc TagImage(TagImageRequest) returns (TagImageResponse) {}
}
message HealthCheckRequest {}
message HealthCheckResponse {
string errmsg = 1;
uint32 cc = 2;
}
message LoginRequest {
string server = 1;
string username = 2;
string password = 3;
}
message LoginResponse {
string errmsg = 1;
uint32 cc = 2;
}
message LogoutRequest {
string server = 1;
}
message LogoutResponse {
string errmsg = 1;
uint32 cc = 2;
}
message ContainerExportRequest {
string name_id = 1;
string output = 2;
uint32 uid = 3;
uint32 gid = 4;
uint32 offset = 5;
}
message ContainerExportResponse {
string errmsg = 1;
uint32 cc = 2;
}
message LoadImageRequest {
string file = 1;
string tag = 2;
}
message LoadImageResponose {
string outmsg = 1;
string errmsg = 2;
uint32 cc = 3;
}
message ImportRequest {
string file = 1;
string tag = 2;
}
message ImportResponose {
string id = 1;
string errmsg = 2;
uint32 cc = 3;
}
message GraphdriverStatusRequest {}
message GraphdriverStatusResponse {
string status = 1;
string errmsg = 2;
uint32 cc = 3;
}
message GraphdriverMetadataRequest {
string name_id = 1;
}
message GraphdriverMetadataResponse {
map<string,string> metadata = 1;
string name = 2;
string errmsg = 3;
uint32 cc = 4;
}
message ContainerFsUsageRequest {
string name_id = 1;
}
message ContainerFsUsageResponse {
string usage = 1;
string errmsg = 2;
uint32 cc = 3;
}
message ContainerUmountRequest {
string name_id = 1;
bool force = 2;
}
message ContainerUmountResponse {
string errmsg = 1;
uint32 cc = 2;
}
message ContainerMountRequest {
string name_id = 1;
}
message ContainerMountResponse {
string errmsg = 1;
uint32 cc = 2;
}
message ContainerRemoveRequest {
string name_id = 1;
}
message ContainerRemoveResponse {
string errmsg = 1;
uint32 cc = 2;
}
message ContainerPrepareRequest {
string image = 1;
string id = 2;
string name = 3;
repeated string storage_opts = 4;
}
message ContainerPrepareResponse {
string mount_point = 1;
string image_conf = 2;
string errmsg = 3;
uint32 cc = 4;
}
message ListContainersRequest {}
message ListContainersResponse {
map<string, bool> containers = 1;
string errmsg = 2;
uint32 cc = 3;
}
// DNSConfig specifies the DNS servers and search domains of a sandbox.
message DNSConfig {
// List of DNS servers of the cluster.
repeated string servers = 1;
// List of DNS search domains of the cluster.
repeated string searches = 2;
// List of DNS options. See https://linux.die.net/man/5/resolv.conf
// for all available options.
repeated string options = 3;
}
enum Protocol {
TCP = 0;
UDP = 1;
}
// PortMapping specifies the port mapping configurations of a sandbox.
message PortMapping {
// Protocol of the port mapping.
Protocol protocol = 1;
// Port number within the container. Default: 0 (not specified).
int32 container_port = 2;
// Port number on the host. Default: 0 (not specified).
int32 host_port = 3;
// Host IP.
string host_ip = 4;
}
enum MountPropagation {
// No mount propagation ("private" in Linux terminology).
PROPAGATION_PRIVATE = 0;
// Mounts get propagated from the host to the container ("rslave" in Linux).
PROPAGATION_HOST_TO_CONTAINER = 1;
// Mounts get propagated from the host to the container and from the
// container to the host ("rshared" in Linux).
PROPAGATION_BIDIRECTIONAL = 2;
}
// Mount specifies a host volume to mount into a container.
message Mount {
// Path of the mount within the container.
string container_path = 1;
// Path of the mount on the host.
string host_path = 2;
// If set, the mount is read-only.
bool readonly = 3;
// If set, the mount needs SELinux relabeling.
bool selinux_relabel = 4;
// Requested propagation mode.
MountPropagation propagation = 5;
}
// NamespaceOption provides options for Linux namespaces.
message NamespaceOption {
// If set, use the host's network namespace.
bool host_network = 1;
// If set, use the host's PID namespace.
bool host_pid = 2;
// If set, use the host's IPC namespace.
bool host_ipc = 3;
}
// Int64Value is the wrapper of int64.
message Int64Value {
// The value.
int64 value = 1;
}
// SELinuxOption are the labels to be applied to the container.
message SELinuxOption {
string user = 1;
string role = 2;
string type = 3;
string level = 4;
}
// LinuxSandboxSecurityContext holds linux security configuration that will be
// applied to a sandbox. Note that:
// 1) It does not apply to containers in the pods.
// 2) It may not be applicable to a PodSandbox which does not contain any running
// process.
message LinuxSandboxSecurityContext {
// Configurations for the sandbox's namespaces.
// This will be used only if the PodSandbox uses namespace for isolation.
NamespaceOption namespace_options = 1;
// Optional SELinux context to be applied.
SELinuxOption selinux_options = 2;
// UID to run sandbox processes as, when applicable.
Int64Value run_as_user = 3;
// If set, the root filesystem of the sandbox is read-only.
bool readonly_rootfs = 4;
// List of groups applied to the first process run in the sandbox, in
// addition to the sandbox's primary GID.
repeated int64 supplemental_groups = 5;
// Indicates whether the sandbox will be asked to run a privileged
// container. If a privileged container is to be executed within it, this
// MUST be true.
// This allows a sandbox to take additional security precautions if no
// privileged containers are expected to be run.
bool privileged = 6;
// Seccomp profile for the sandbox, candidate values are:
// * docker/default: the default profile for the docker container runtime
// * unconfined: unconfined profile, ie, no seccomp sandboxing
// * localhost/<full-path-to-profile>: the profile installed on the node.
// <full-path-to-profile> is the full path of the profile.
// Default: "", which is identical with unconfined.
string seccomp_profile_path = 7;
}
// LinuxPodSandboxConfig holds platform-specific configurations for Linux
// host platforms and Linux-based containers.
message LinuxPodSandboxConfig {
// Parent cgroup of the PodSandbox.
// The cgroupfs style syntax will be used, but the container runtime can
// convert it to systemd semantics if needed.
string cgroup_parent = 1;
// LinuxSandboxSecurityContext holds sandbox security attributes.
LinuxSandboxSecurityContext security_context = 2;
// Sysctls holds linux sysctls config for the sandbox.
map<string, string> sysctls = 3;
}
// PodSandboxMetadata holds all necessary information for building the sandbox name.
// The container runtime is encouraged to expose the metadata associated with the
// PodSandbox in its user interface for better user experience. For example,
// the runtime can construct a unique PodSandboxName based on the metadata.
message PodSandboxMetadata {
// Pod name of the sandbox. Same as the pod name in the PodSpec.
string name = 1;
// Pod UID of the sandbox. Same as the pod UID in the PodSpec.
string uid = 2;
// Pod namespace of the sandbox. Same as the pod namespace in the PodSpec.
string namespace = 3;
// Attempt number of creating the sandbox. Default: 0.
uint32 attempt = 4;
}
// PodSandboxConfig holds all the required and optional fields for creating a
// sandbox.
message PodSandboxConfig {
// Metadata of the sandbox. This information will uniquely identify the
// sandbox, and the runtime should leverage this to ensure correct
// operation. The runtime may also use this information to improve UX, such
// as by constructing a readable name.
PodSandboxMetadata metadata = 1;
// Hostname of the sandbox.
string hostname = 2;
// Path to the directory on the host in which container log files are
// stored.
// By default the log of a container going into the LogDirectory will be
// hooked up to STDOUT and STDERR. However, the LogDirectory may contain
// binary log files with structured logging data from the individual
// containers. For example, the files might be newline separated JSON
// structured logs, systemd-journald journal files, gRPC trace files, etc.
// E.g.,
// PodSandboxConfig.LogDirectory = `/var/log/pods/<podUID>/`
// ContainerConfig.LogPath = `containerName_Instance#.log`
//
// WARNING: Log management and how kubelet should interface with the
// container logs are under active discussion in
// https://issues.k8s.io/24677. There *may* be future change of direction
// for logging as the discussion carries on.
string log_directory = 3;
// DNS config for the sandbox.
DNSConfig dns_config = 4;
// Port mappings for the sandbox.
repeated PortMapping port_mappings = 5;
// Key-value pairs that may be used to scope and select individual resources.
map<string, string> labels = 6;
// Unstructured key-value map that may be set by the kubelet to store and
// retrieve arbitrary metadata. This will include any annotations set on a
// pod through the Kubernetes API.
//
// Annotations MUST NOT be altered by the runtime; the annotations stored
// here MUST be returned in the PodSandboxStatus associated with the pod
// this PodSandboxConfig creates.
//
// In general, in order to preserve a well-defined interface between the
// kubelet and the container runtime, annotations SHOULD NOT influence
// runtime behaviour.
//
// Annotations can also be useful for runtime authors to experiment with
// new features that are opaque to the Kubernetes APIs (both user-facing
// and the CRI). Whenever possible, however, runtime authors SHOULD
// consider proposing new typed fields for any new features instead.
map<string, string> annotations = 7;
// Optional configurations specific to Linux hosts.
LinuxPodSandboxConfig linux = 8;
}
// ImageSpec is an internal representation of an image. Currently, it wraps the
// value of a Container's Image field (e.g. imageID or imageDigest), but in the
// future it will include more detailed information about the different image types.
message ImageSpec {
string image = 1;
}
message ImageFilter {
// Spec of the image.
ImageSpec image = 1;
}
message ListImagesRequest {
// Filter to list images.
ImageFilter filter = 1;
bool check = 2;
}
message HealthCheck {
repeated string test = 1;
int64 interval = 2;
int64 timeout = 3;
int64 start_period = 4;
int32 retries = 5;
bool exit_on_unhealthy = 6;
}
// Basic information about a container image.
message Image {
// ID of the image.
string id = 1;
// Other names by which this image is known.
repeated string repo_tags = 2;
// Digests by which this image is known.
repeated string repo_digests = 3;
// Size of the image in bytes. Must be > 0.
uint64 size = 4;
// UID that will run the command(s). This is used as a default if no user is
// specified when creating the container. UID and the following user name
// are mutually exclusive.
Int64Value uid = 5;
// User name that will run the command(s). This is used if UID is not set
// and no user is specified when creating container.
string username = 6;
// Create time of image
string created = 7;
// Load time of image
string loaded = 8;
// oci image spec
ImageSpec spec = 9;
// Health check
HealthCheck healthcheck = 10;
}
message ListImagesResponse {
// List of images.
repeated Image images = 1;
string errmsg = 2;
uint32 cc = 3;
}
message ImageStatusRequest {
// Spec of the image.
ImageSpec image = 1;
// Verbose indicates whether to return extra information about the image.
bool verbose = 2;
}
message ImageStatusResponse {
// Status of the image.
Image image = 1;
// Info is extra information of the Image. The key could be abitrary string, and
// value should be in json format. The information could include anything useful
// for debug, e.g. image config for oci image based container runtime.
// It should only be returned non-empty when Verbose is true.
map<string, string> info = 2;
string errmsg = 3;
uint32 cc = 4;
}
message ImageInfoRequest {
ImageSpec image = 1;
}
message ImageInfoResponse {
string spec = 1;
string errmsg = 2;
uint32 cc = 3;
}
// AuthConfig contains authorization information for connecting to a registry.
message AuthConfig {
string username = 1;
string password = 2;
string auth = 3;
string server_address = 4;
// IdentityToken is used to authenticate the user and get
// an access token for the registry.
string identity_token = 5;
// RegistryToken is a bearer token to be sent to a registry
string registry_token = 6;
}
message PullImageRequest {
// Spec of the image.
ImageSpec image = 1;
// Authentication configuration for pulling the image.
AuthConfig auth = 2;
// Config of the PodSandbox, which is used to pull image in PodSandbox context.
PodSandboxConfig sandbox_config = 3;
}
message PullImageResponse {
// Reference to the image in use. For most runtimes, this should be an
// image ID or digest.
string image_ref = 1;
string errmsg = 2;
uint32 cc = 3;
}
message RemoveImageRequest {
// Spec of the image to remove.
ImageSpec image = 1;
bool force = 2;
}
message RemoveImageResponse {
string errmsg = 1;
uint32 cc = 2;
}
message ImageFsInfoRequest {}
// UInt64Value is the wrapper of uint64.
message UInt64Value {
// The value.
uint64 value = 1;
}
// StorageIdentifier uniquely identify the storage..
message StorageIdentifier{
// UUID of the device.
string uuid = 1;
}
// FilesystemUsage provides the filesystem usage information.
message FilesystemUsage {
// Timestamp in nanoseconds at which the information were collected. Must be > 0.
int64 timestamp = 1;
// The underlying storage of the filesystem.
StorageIdentifier storage_id = 2;
// UsedBytes represents the bytes used for images on the filesystem.
// This may differ from the total bytes used on the filesystem and may not
// equal CapacityBytes - AvailableBytes.
UInt64Value used_bytes = 3;
// InodesUsed represents the inodes used by the images.
// This may not equal InodesCapacity - InodesAvailable because the underlying
// filesystem may also be used for purposes other than storing images.
UInt64Value inodes_used = 4;
}
message ImageFsInfoResponse {
// Information of image filesystem(s).
repeated FilesystemUsage image_filesystems = 1;
string errmsg = 2;
uint32 cc = 3;
}
message TagImageRequest {
ImageSpec srcName = 1;
ImageSpec destName = 2;
}
message TagImageResponse {
string errmsg = 1;
uint32 cc = 2;
}
\ No newline at end of file
/*
* Copyright (c) 2020 Huawei Technologies Co., Ltd.
* isula-transform is licensed under 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.
* Create: 2020-04-24
*/
package main
import "github.com/urfave/cli"
var basicFlags = []cli.Flag{
cli.StringFlag{
Name: "log",
Usage: "specific output log file path",
Value: "/var/log/isula-kits/transform.log",
},
cli.StringFlag{
Name: "log-level",
Usage: "Customize the level of logging for collection, allowed: debug, info, warn, error",
Value: "info",
},
cli.StringFlag{
Name: "container-type",
Usage: "origin container type",
Value: "docker",
Hidden: true,
},
cli.StringFlag{
Name: "isulad-config-file",
Usage: "iSulad configuration file path",
Value: "/etc/isulad/daemon.json",
},
}
var dockerFlags = []cli.Flag{
cli.StringFlag{
Name: "docker-graph",
Usage: "graph root of docker",
Value: "/var/lib/docker",
},
cli.StringFlag{
Name: "docker-state",
Usage: "state root of docker",
Value: "/var/run/docker",
},
}
var containerFlags = []cli.Flag{
cli.BoolFlag{
Name: "all",
Usage: "transform all containers",
},
}
var transformFlags = [][]cli.Flag{basicFlags, dockerFlags, containerFlags}
module isula.org/isula-transform
replace github.com/docker/docker => github.com/docker/engine v0.0.0-20190822180741-9552f2b2fdde
require (
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
github.com/Microsoft/go-winio v0.4.14 // indirect
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/docker/distribution v2.7.1+incompatible // indirect
github.com/docker/docker v0.0.0-00010101000000-000000000000
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/gogo/protobuf v1.2.1 // indirect
github.com/golang/mock v1.4.3
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0
github.com/google/go-cmp v0.4.0
github.com/gorilla/mux v1.7.4 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/opencontainers/runtime-spec v1.0.1
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.4.2
github.com/smartystreets/goconvey v1.6.4
github.com/stretchr/testify v1.4.0 // indirect
github.com/urfave/cli v1.22.4
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0 // indirect
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae
golang.org/x/text v0.3.2 // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce // indirect
google.golang.org/grpc v1.29.0
google.golang.org/protobuf v1.21.0
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0
gotest.tools v2.2.0+incompatible // indirect
)
go 1.13
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/engine v0.0.0-20190822180741-9552f2b2fdde h1:6JcsC3tTf9ISGL2xesRNf0vQ/RgpVXXNYz/IFXQoqX0=
github.com/docker/engine v0.0.0-20190822180741-9552f2b2fdde/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0 h1:aRz0NBceriICVtjhCgKkDvl+RudKu1CT6h0ZvUTrNfE=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runtime-spec v1.0.1 h1:wY4pOY8fBdSIvs9+IDHC55thBuEulhzfSgKeC1yFvzQ=
github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA=
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0 h1:MsuvTghUPjX762sGLnGsxC3HM0B5r83wEtYcYR8/vRs=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262 h1:qsl9y/CJx34tuA7QCPNp86JNJe4spst6Ff8MjvPUdPg=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce h1:1mbrb1tUU+Zmt5C94IGKADBTJZjZXAd+BubWi7r9EiI=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.29.0 h1:2pJjwYOdkZ9HlN4sWRYBg9ttH5bCOlsueaM+b/oYjwo=
google.golang.org/grpc v1.29.0/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
木兰宽松许可证, 第2版
木兰宽松许可证, 第2版
2020年1月 http://license.coscl.org.cn/MulanPSL2
您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束:
0. 定义
“软件”是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。
“贡献”是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。
“贡献者”是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。
“法人实体”是指提交贡献的机构及其“关联实体”。
“关联实体”是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。
1. 授予版权许可
每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。
2. 授予专利许可
每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。
3. 无商标许可
“本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用除外。
4. 分发限制
您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。
5. 免责声明与责任限制
“软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。
6. 语言
“本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。
条款结束
如何将木兰宽松许可证,第2版,应用到您的软件
如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步:
1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字;
2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中;
3, 请将如下声明文本放入每个源文件的头部注释中。
Copyright (c) [Year] [name of copyright holder]
[Software Name] is licensed under 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.
Mulan Permissive Software License,Version 2
Mulan Permissive Software License,Version 2 (Mulan PSL v2)
January 2020 http://license.coscl.org.cn/MulanPSL2
Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions:
0. Definition
Software means the program and related documents which are licensed under this License and comprise all Contribution(s).
Contribution means the copyrightable work licensed by a particular Contributor under this License.
Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License.
Legal Entity means the entity making a Contribution and all its Affiliates.
Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity.
1. Grant of Copyright License
Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not.
2. Grant of Patent License
Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken.
3. No Trademark License
No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in Section 4.
4. Distribution Restriction
You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software.
5. Disclaimer of Warranty and Limitation of Liability
THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW IT’S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
6. Language
THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL.
END OF THE TERMS AND CONDITIONS
How to Apply the Mulan Permissive Software License,Version 2 (Mulan PSL v2) to Your Software
To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps:
i Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner;
ii Create a file named “LICENSE” which contains the whole context of this License in the first directory of your software package;
iii Attach the statement to the appropriate annotated syntax at the beginning of each source file.
Copyright (c) [Year] [name of copyright holder]
[Software Name] is licensed under 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.
此差异已折叠。
/*
* Copyright (c) 2020 Huawei Technologies Co., Ltd.
* isula-transform is licensed under 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.
* Create: 2020-04-24
*/
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
"gopkg.in/natefinch/lumberjack.v2"
"isula.org/isula-transform/transform"
_ "isula.org/isula-transform/transform/register"
"isula.org/isula-transform/utils"
)
const (
exitNormal = iota
exitInitErr
exitTransformErr
maxConcurrentTransform = 128
maxPerLogFileSize = 10 // megabytes
)
var (
version string
gitCommit string
)
func genVersion() string {
versions := []string{
"version:" + version,
"commit:" + gitCommit,
}
return strings.Join(versions, "\t")
}
func main() {
app := &cli.App{
Name: "isula-transform",
Usage: "transform specify docker container type configuration to iSulad type",
UsageText: "[global options] --all|container_id[ container_id...]",
Version: genVersion(),
Action: start,
}
for _, v := range transformFlags {
app.Flags = append(app.Flags, v...)
}
if err := app.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(exitInitErr)
}
os.Exit(exitNormal)
}
func start(ctx *cli.Context) error {
logInit(ctx)
if err := transformInit(ctx); err != nil {
return cli.NewExitError(err.Error(), exitInitErr)
}
return doTransform(ctx)
}
func logInit(ctx *cli.Context) {
logPath := ctx.GlobalString("log")
logRoot := filepath.Dir(logPath)
if err := os.MkdirAll(logRoot, 0660); err != nil {
logrus.SetOutput(os.Stdout)
logrus.Infof("create the log directory %s failed: %v, using STDOUT", logRoot, err)
} else {
logrus.SetOutput(&lumberjack.Logger{
Filename: logPath,
MaxSize: maxPerLogFileSize,
Compress: true,
})
}
logrus.SetLevel(transLogLevel(ctx.GlobalString("log-level")))
}
func transLogLevel(lvl string) logrus.Level {
switch strings.ToLower(lvl) {
case "error":
return logrus.ErrorLevel
case "warn":
return logrus.WarnLevel
case "debug":
return logrus.DebugLevel
default:
}
return logrus.InfoLevel
}
func transformInit(ctx *cli.Context) error {
var iSuladCfg = struct {
Graph string `json:"graph"`
State string `json:"state"`
Runtime string `json:"default-runtime"`
LogLevel string `json:"log-level"`
LogDriver string `json:"log-driver"`
StorageDriver string `json:"storage-driver"`
ImageServer string `json:"image-server-sock-addr"`
}{}
iSuladCfgFile := ctx.GlobalString("isulad-config-file")
if err := utils.CheckFileValid(iSuladCfgFile); err != nil {
return errors.Wrapf(err, "check isulad daemon config failed")
}
iSuladCfgData, err := ioutil.ReadFile(iSuladCfgFile)
if err != nil {
logrus.Errorf("read isulad daemon config failed: %v, file path: %s", err, iSuladCfgFile)
return errors.Wrapf(err, "read isulad daemon config failed")
}
err = json.Unmarshal(iSuladCfgData, &iSuladCfg)
if err != nil {
logrus.Errorf("unmarshal isulad daemon config failed: %v, file path: %s", err, iSuladCfgFile)
return errors.Wrapf(err, "unmarshal isulad daemon config failed")
}
logrus.Debugf("isulad daemon config: %+v", iSuladCfg)
err = transform.InitIsuladTool(iSuladCfg.Graph, iSuladCfg.Runtime, iSuladCfg.StorageDriver, iSuladCfg.ImageServer)
if err != nil {
return errors.Wrapf(err, "transform init failed")
}
if iSuladCfg.LogDriver != "file" {
logrus.Infof("isula daemon log driver is %s, can't redirect to file", iSuladCfg.LogDriver)
} else {
transform.LcrLogInit(iSuladCfg.State, iSuladCfg.Runtime, iSuladCfg.LogLevel)
}
return nil
}
func doTransform(ctx *cli.Context) error {
e := transform.GetTransformer(ctx)
if e == nil {
return cli.NewExitError("get transform engine failed", exitInitErr)
}
if err := e.Init(); err != nil {
return cli.NewExitError("transform engine init failed", exitInitErr)
}
var ids []string
all := ctx.GlobalBool("all")
if !all {
if ctx.Args().Present() {
ids = append(ctx.Args().Tail(), ctx.Args().First())
} else {
exitMsg := "isula-transform requires at least one container id as an input or setting the --all flag"
return cli.NewExitError(exitMsg, exitInitErr)
}
}
exitCode := exitNormal
retCh := make(chan transform.Result, maxConcurrentTransform)
go e.Transform(ids, all, retCh)
for ret := range retCh {
if !ret.Ok {
exitCode = exitTransformErr
fmt.Fprintln(os.Stderr, ret.Msg)
} else {
fmt.Fprintln(os.Stdout, ret.Msg)
}
}
if exitCode != exitNormal {
return cli.NewExitError("The transformation has been completed, but at least one failed", exitTransformErr)
}
return nil
}
/*
* Copyright (c) 2020 Huawei Technologies Co., Ltd.
* isula-transform is licensed under 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.
* Create: 2020-04-24
*/
package docker
import (
"context"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/docker/docker/api/types/container"
"github.com/sirupsen/logrus"
"isula.org/isula-transform/transform"
"isula.org/isula-transform/types"
)
const (
changeItem = iota
addItem
delItem
ignoreItem
)
type diffNode struct {
// parent *diffNode
children map[string]*diffNode
operation uint8
path string
}
type diffTrie struct {
root *diffNode
}
func newTrie() *diffTrie {
return &diffTrie{
root: &diffNode{
operation: ignoreItem,
path: "/",
children: make(map[string]*diffNode),
},
}
}
func (t *diffTrie) insert(path string, operation uint8) {
path = strings.TrimPrefix(path, "/")
pathItems := strings.Split(path, "/")
curNode := t.root
for len(pathItems) > 0 {
curPath := pathItems[0]
node, ok := curNode.children[curPath]
if !ok {
node = &diffNode{
operation: operation,
path: filepath.Join(curNode.path, curPath),
children: make(map[string]*diffNode),
}
curNode.children[curPath] = node
} else {
node.operation = ignoreItem
}
curNode = node
pathItems = pathItems[1:]
}
}
func (t *diffTrie) filter() []container.ContainerChangeResponseItem {
var stack []*diffNode
var diffs []container.ContainerChangeResponseItem
node := t.root
for node == nil {
return nil
}
stack = append(stack, node)
for len(stack) > 0 {
node := stack[0]
if node.operation != ignoreItem {
diffs = append(diffs, container.ContainerChangeResponseItem{
Kind: node.operation,
Path: node.path,
})
}
for _, v := range node.children {
stack = append(stack, v)
}
stack = stack[1:]
}
return diffs
}
type deviceMapperDriver struct {
transform.BaseStorageDriver
client dockerClient
}
func newDeviceMapperDriver(base transform.BaseStorageDriver, client dockerClient) transform.StorageDriver {
return &deviceMapperDriver{BaseStorageDriver: base, client: client}
}
func (dm *deviceMapperDriver) GenerateRootFs(id, image string) (string, error) {
return dm.BaseStorageDriver.GenerateRootFs(id, image)
}
func (dm *deviceMapperDriver) TransformRWLayer(ctr *types.IsuladV2Config, oldRootFs string) error {
// mount
if err := dm.BaseStorageDriver.MountRootFs(ctr.CommonConfig.ID); err != nil {
return err
}
// umount
defer func() {
if err := dm.BaseStorageDriver.UmountRootFs(ctr.CommonConfig.ID); err != nil {
logrus.Infof("device mapper umount rootfs failed: %v", err)
}
}()
// get diff
diff, err := dm.client.ContainerDiff(context.Background(), ctr.CommonConfig.ID)
if err != nil {
return err
}
changes := dm.changesFilter(diff, ctr.CommonConfig.MountPoints)
logrus.Infof("device mapper driver get diff form docker: %+v, filter: %+v", diff, changes)
for idx := range changes {
src := oldRootFs + changes[idx].Path
dest := ctr.CommonConfig.BaseFs + changes[idx].Path
switch changes[idx].Kind {
case addItem, changeItem:
// cp
destRoot := filepath.Dir(dest)
if err := exec.Command("cp", "-ra", src, destRoot).Run(); err != nil {
logrus.Errorf("device mapper copy %s to %s filed: %v", src, dest, err)
return err
}
case delItem:
// delete
if err := os.RemoveAll(dest); err != nil {
logrus.Errorf("device mapper remover %s filed: %v", dest, err)
return err
}
default:
}
}
return nil
}
/*
A /xxx/.../something ==> root dir /xxx C
D /xxx/.../something ==> root dir /xxx C
C /xxx/.../something ==> root dir /xxx C
1. We ignore the parent folder node /xxx/..., so if the properties of the folder
are modified at the same time, the change is lost.
2. only if the node didn't have any children nodes, we copy or delete it. In
another words, We only deal with leaf nodes.
note: filter path which match bind mount in container
*/
func (dm *deviceMapperDriver) changesFilter(changes []container.ContainerChangeResponseItem,
mounts map[string]types.Mount) []container.ContainerChangeResponseItem {
// create trie
t := newTrie()
for _, change := range changes {
if _, ok := mounts[change.Path]; ok {
continue
}
t.insert(change.Path, change.Kind)
}
return t.filter()
}
func (dm *deviceMapperDriver) Cleanup(id string) {
dm.BaseStorageDriver.CleanupRootFs(id)
}
/*
* Copyright (c) 2020 Huawei Technologies Co., Ltd.
* isula-transform is licensed under 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.
* Create: 2020-04-24
*/
package docker
import (
"sort"
"testing"
"github.com/docker/docker/api/types/container"
. "github.com/google/go-cmp/cmp"
. "github.com/smartystreets/goconvey/convey"
"isula.org/isula-transform/types"
)
func Test_deviceMapperDriver_changesFilter(t *testing.T) {
Convey("Test_deviceMapperDriver_changesFilter", t, func() {
dm := &deviceMapperDriver{}
testDiff := []container.ContainerChangeResponseItem{
{Kind: changeItem, Path: "/etc"}, // root filter
{Kind: delItem, Path: "/etc/delfile"}, // delete save
{Kind: changeItem, Path: "/etc/os-release"}, // change save
{Kind: changeItem, Path: "/root"}, // root filter
{Kind: addItem, Path: "/root/add"}, // add save
{Kind: addItem, Path: "/root/padd"}, // parent filter
{Kind: addItem, Path: "/root/padd/subadd"}, // add save
{Kind: addItem, Path: "/root/mount"}, // mount filter
}
testMount := map[string]types.Mount{
"/root/mount": {Destination: "/root/mount"},
}
expect := []container.ContainerChangeResponseItem{
{Kind: delItem, Path: "/etc/delfile"}, // delete save
{Kind: changeItem, Path: "/etc/os-release"}, // change save
{Kind: addItem, Path: "/root/add"}, // add save
{Kind: addItem, Path: "/root/padd/subadd"}, // add save
}
got := dm.changesFilter(testDiff, testMount)
sortChanges := func(changes []container.ContainerChangeResponseItem) {
sort.SliceStable(changes, func(i, j int) bool {
return changes[i].Path < changes[j].Path
})
}
sortChanges(expect)
sortChanges(got)
So(Diff(got, expect), ShouldBeBlank)
})
}
/*
* Copyright (c) 2020 Huawei Technologies Co., Ltd.
* isula-transform is licensed under 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.
* Create: 2020-04-24
*/
// Package docker implement transformer for transform docker container
package docker
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net"
"net/http"
"os"
"os/exec"
"os/signal"
"path/filepath"
"strings"
"sync"
"syscall"
"time"
"github.com/docker/docker/api/types/container"
docker "github.com/docker/docker/client"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
"golang.org/x/sys/unix"
"isula.org/isula-transform/transform"
"isula.org/isula-transform/types"
"isula.org/isula-transform/utils"
)
type containerStatus int
const (
notExist containerStatus = iota
hasBeenTransformed
needTransform
)
var (
defaultDockerHostProto = "unix"
defaultDockerHostAddr = "/var/run/docker.sock"
defaultExecRoot = "/var/run/docker" // Root directory for execution state files
defaultDataRoot = "/var/lib/docker" // Root directory of the Docker runtime
containerdRuntime = "io.containerd.runtime.v1.linux"
containerdNameSpace = "moby"
defaultTimeout = 10 * time.Second
containerIDLen = 64
)
type dockerClient interface {
ContainerDiff(context.Context, string) ([]container.ContainerChangeResponseItem, error)
ContainerPause(context.Context, string) error
}
type dockerTransformer struct {
ctrs *sync.Map
client dockerClient
sd transform.StorageDriver
transform.BaseTransformer
}
func init() {
transform.Register("docker", New)
}
// New return a transform engine for docker container
func New(ctx *cli.Context) transform.Transformer {
var opts []transform.EngineOpt
graphRoot := ctx.GlobalString("docker-graph")
stateRoot := ctx.GlobalString("docker-state")
opts = append(opts, transform.EngineWithGraph(graphRoot), transform.EngineWithState(stateRoot))
return newWithConfig(opts...)
}
// newWithConfig create a transform engine for docker container with specific config
func newWithConfig(opts ...transform.EngineOpt) transform.Transformer {
var e dockerTransformer
for _, o := range opts {
o(&e.BaseTransformer)
}
if e.StateRoot == "" {
e.StateRoot = defaultExecRoot
}
if e.GraphRoot == "" {
e.GraphRoot = defaultDataRoot
}
e.Name = "docker"
return &e
}
func (t *dockerTransformer) Init() error {
var retErr error
c := &http.Client{
Timeout: 2 * defaultTimeout,
Transport: &http.Transport{
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
return net.DialTimeout(defaultDockerHostProto, defaultDockerHostAddr, defaultTimeout)
},
DisableCompression: true,
},
CheckRedirect: docker.CheckRedirect,
}
t.client, retErr = docker.NewClientWithOpts(docker.WithHTTPClient(c))
if retErr != nil {
logrus.Errorf("create docker client failed: %v", retErr)
return errors.Wrap(retErr, "create docker client failed")
}
t.sd, retErr = t.initStorageDriver()
if retErr != nil {
logrus.Errorf("init storage driver failed: %v", retErr)
return errors.Wrap(retErr, "init storage driver failed")
}
return t.initContainers()
}
func (t *dockerTransformer) Transform(ids []string, all bool, retCh chan transform.Result) {
if all {
t.ctrs.Range(func(k, _ interface{}) bool {
id, _ := k.(string)
ids = append(ids, id)
return true
})
}
signalWg := new(sync.WaitGroup)
signalCtx := t.handleSignal()
var wg sync.WaitGroup
for _, ctr := range ids {
wg.Add(1)
go func(id string) {
var ret transform.Result
switch ctrID, ok := t.matchID(id); ok {
case notExist:
ret.Msg = fmt.Sprintf("transform %s: container was not found", id)
case hasBeenTransformed:
ret.Ok = true
ret.Msg = fmt.Sprintf("transform %s: container has been transformed", id)
case needTransform:
err := t.transform(ctrID, newRollback(signalCtx, signalWg))
if err != nil {
ret.Msg = fmt.Sprintf("transform %s: %s", id, err.Error())
} else {
ret.Ok = true
ret.Msg = fmt.Sprintf("transform %s: success", id)
}
default:
}
retCh <- ret
wg.Done()
}(ctr)
}
wg.Wait()
signalWg.Wait()
close(retCh)
}
func (t *dockerTransformer) transform(id string, rb *rollback) error {
var (
retErr error
hostCfg *types.IsuladHostConfig
logCfg *container.LogConfig
v2Cfg *types.IsuladV2Config
ociCfg *specs.Spec
oldRootFs string
)
rb.wait()
defer func() {
if retErr != nil {
rb.run()
}
rb.close()
}()
logrus.Infof("start to transform %s", id)
// before transform, pause container to suspend all processes in a container
retErr = t.client.ContainerPause(context.Background(), id)
if retErr != nil && !strings.Contains(retErr.Error(), "already paused") {
logrus.Errorf("pause container %s failed: %v", id, retErr)
return errors.Wrap(retErr, "pause container")
}
// init
iSulad := transform.GetIsuladCfgTool()
retErr = iSulad.PrepareBundleDir(id)
if retErr != nil {
logrus.Errorf("prepare bundle dir failed: %v", retErr)
return errors.Wrap(retErr, "prepare root dir")
}
rb.register(func() {
logrus.Infof("rollback: clean up bundle dir of container %s", id)
if err := iSulad.Cleanup(id); err != nil {
logrus.Warnf("rollback: clean up bundle dir of container %s: %v", id, err)
}
})
// start transform
// transform hostConfig: hostconfig.json
hostCfg, logCfg, retErr = t.transformHostConfig(id)
if retErr != nil {
logrus.Errorf("transform hostconfig failed: %v", retErr)
return errors.Wrap(retErr, "transform hostconfig")
}
// transform config.v2: config.v2.json
reconcileOpts := append(genV2OptsFromHostCfg(hostCfg),
v2ConfigWithLogConfig(logCfg, filepath.Join(iSulad.GetRuntimePath(), id)))
v2Cfg, retErr = t.transformV2Config(id, reconcileOpts...)
if retErr != nil {
logrus.Errorf("transform configV2 failed: %v", retErr)
return errors.Wrap(retErr, "transform configV2")
}
rb.register(func() {
logrus.Infof("rollback: clean up storage register of container %s", id)
t.sd.Cleanup(id)
})
// share shm : mounts/shm
retErr = iSulad.PrepareShm(v2Cfg.CommonConfig.ShmPath, hostCfg.ShmSize)
if retErr != nil {
logrus.Errorf("prepare share shm failed: %v", retErr)
return errors.Wrap(retErr, "prepare share shm")
}
rb.register(func() {
logrus.Infof("rollback: umount share shm of container %s path %s", id, v2Cfg.CommonConfig.ShmPath)
if umountErr := unix.Unmount(v2Cfg.CommonConfig.ShmPath, unix.MNT_DETACH); umountErr != nil {
logrus.Warnf("umount %s err: %v", v2Cfg.CommonConfig.ShmPath, umountErr)
}
})
// copy linux network files : hostname hosts resolve.conf
files := []string{types.Hostname, types.Hosts, types.Resolv}
for idx := range files {
srcF := v2Cfg.CommonConfig.GetOriginNetworkFile(files[idx])
destF := iSulad.GetNetworkFilePath(id, files[idx])
retErr = exec.Command("cp", "-a", srcF, destF).Run()
if retErr != nil {
logrus.Errorf("copy %s to %s failed", srcF, destF)
return errors.Wrapf(retErr, "copy %s to %s failed", srcF, destF)
}
}
// oci spec: config.json
ociCfg, oldRootFs, retErr = t.transformOciConfig(id, v2Cfg.CommonConfig, hostCfg)
if retErr != nil {
logrus.Errorf("transform oci spec config failed: %v", retErr)
return errors.Wrap(retErr, "transform oci spec")
}
// copy RWlayer
retErr = t.sd.TransformRWLayer(v2Cfg, oldRootFs)
if retErr != nil {
logrus.Errorf("storage driver transform RWLayer failed: %v", retErr)
return errors.Wrap(retErr, "transform RWLayer")
}
// lcr_create: config ocihooks.json seccomp
ociCfgData, err := json.Marshal(ociCfg)
if err != nil {
logrus.Errorf("marshal oci config failed: %s", err)
return errors.Wrap(err, "marshal oci config")
}
retErr = iSulad.LcrCreate(id, ociCfgData)
if retErr != nil {
logrus.Error("lcr create failed")
return retErr
}
logrus.Infof("transform %s successfully", id)
return nil
}
func (t *dockerTransformer) transformHostConfig(id string) (*types.IsuladHostConfig, *container.LogConfig, error) {
var isuladHostCfg types.IsuladHostConfig
var l container.LogConfig
var hostCfg = struct {
*types.IsuladHostConfig
LogConfig *container.LogConfig
}{
&isuladHostCfg,
&l,
}
// load
hostCfgPath := filepath.Join(t.GraphRoot, "containers", id, types.Hostconfig)
err := utils.CheckFileValid(hostCfgPath)
if err != nil {
logrus.Errorf("check docker hostconfig.json failed: %v", err)
return nil, nil, errors.Wrap(err, "check docker hostconfig.json")
}
data, err := ioutil.ReadFile(hostCfgPath)
if err != nil {
logrus.Errorf("read container %s's host config file %s failed: %v", id, hostCfgPath, err)
return nil, nil, errors.Wrap(err, "read hostconfig.json")
}
// Unmarshal to types.IsuladHostConfig
err = json.Unmarshal(data, &hostCfg)
if err != nil {
logrus.Errorf("can't unmarshal container %s's host config to iSulad type: %v", id, err)
return nil, nil, errors.Wrap(err, "unmarshal host config data")
}
// reconcile and save
iSulad := transform.GetIsuladCfgTool()
reconcileHostConfig(&isuladHostCfg, iSulad.Runtime())
err = iSulad.SaveConfig(id, &isuladHostCfg, iSulad.MarshalIndent, iSulad.GetHostCfgPath)
if err != nil {
logrus.Errorf("save host config to file %s failed", iSulad.GetHostCfgPath(id))
return nil, nil, errors.Wrap(err, "save hostconfig.json")
}
return &isuladHostCfg, &l, nil
}
func (t *dockerTransformer) loadV2Config(id string) (*types.DockerV2Config, error) {
var dockerV2Cfg = types.DockerV2Config{}
v2Path := filepath.Join(t.GraphRoot, "containers", id, types.V2config)
err := utils.CheckFileValid(v2Path)
if err != nil {
logrus.Errorf("check docker config.v2.json failed: %v", err)
return nil, errors.Wrap(err, "check docker config.v2.json")
}
data, err := ioutil.ReadFile(v2Path)
if err != nil {
logrus.Errorf("can't read container %s's v2 config file %s with %v", id, v2Path, err)
return nil, errors.Wrap(err, "read config.v2.json")
}
err = json.Unmarshal(data, &dockerV2Cfg)
if err != nil {
logrus.Errorf("can't unmarshal container %s's v2 config %s with %v", id, v2Path, err)
return nil, errors.Wrap(err, "unmarshal v2 config data")
}
return &dockerV2Cfg, nil
}
func (t *dockerTransformer) transformV2Config(id string, opts ...v2ConfigReconcileOpt) (*types.IsuladV2Config, error) {
var (
iSuladState = types.ContainerState{}
iSuladCommon = types.CommonConfig{}
iSuladV2Cfg = types.IsuladV2Config{
CommonConfig: &iSuladCommon,
Image: "",
State: &iSuladState,
}
iSulad = transform.GetIsuladCfgTool()
)
// load
ctr, err := t.loadV2Config(id)
if err != nil {
logrus.Errorf("load container %s's v2 config failed: %v", id, err)
return nil, errors.Wrap(err, "load container config")
}
commonData, err := json.Marshal(ctr)
if err != nil {
logrus.Errorf("internal error: %v", err)
return nil, errors.Wrap(err, "common data internal error")
}
err = json.Unmarshal(commonData, &iSuladCommon)
if err != nil {
logrus.Errorf("internal error: %v", err)
return nil, errors.Wrap(err, "common config internal error")
}
stateData, err := json.Marshal(ctr.State)
if err != nil {
logrus.Errorf("internal error: %v", err)
return nil, errors.Wrap(err, "state data internal error")
}
err = json.Unmarshal(stateData, &iSuladState)
if err != nil {
logrus.Errorf("internal error: %v", err)
return nil, errors.Wrap(err, "state config internal error")
}
iSuladV2Cfg.Image = ctr.ImageID
basePath := filepath.Join(iSulad.GetRuntimePath(), id)
opts = append(opts, []v2ConfigReconcileOpt{
v2ConfigWithImage(ctr.Config.Image),
v2ConfigWithCgroupParent(ctr.CgroupParent),
}...)
reconcileV2Config(&iSuladV2Cfg, basePath, opts...)
iSuladCommon.BaseFs, err = t.sd.GenerateRootFs(id, iSuladCommon.Image)
if err != nil {
logrus.Errorf("storage driver generate new rootfs failed: %v", err)
return nil, errors.Wrap(err, "generate new rootfs")
}
// save
err = iSulad.SaveConfig(id, &iSuladV2Cfg, iSulad.MarshalIndent, iSulad.GetConfigV2Path)
if err != nil {
logrus.Errorf("save v2 config to file %s failed", iSulad.GetConfigV2Path(id))
return nil, errors.Wrap(err, "save config.v2.json")
}
return &iSuladV2Cfg, nil
}
func (t *dockerTransformer) transformOciConfig(id string,
commonCfg *types.CommonConfig, hostCfg *types.IsuladHostConfig) (*specs.Spec, string, error) {
var ociConfig = specs.Spec{}
// load
// path like: /var/run/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/ctr_id/config.json
ociPath := filepath.Join(t.StateRoot, "containerd/daemon", containerdRuntime, containerdNameSpace, id, types.Ociconfig)
err := utils.CheckFileValid(ociPath)
if err != nil {
logrus.Errorf("check docker config.json failed: %v", err)
return nil, "", errors.Wrap(err, "check docker config.json")
}
data, err := ioutil.ReadFile(ociPath)
if err != nil {
logrus.Errorf("can't read oci config file, check if the container %s is running", id)
return nil, "", errors.Wrap(err, "read oci config.json")
}
err = json.Unmarshal(data, &ociConfig)
if err != nil {
logrus.Errorf("can't unmarshal container %s's ociconfig %s with %v", id, ociPath, err)
return nil, "", errors.Wrap(err, "unmarshal oci config data")
}
// reconcile
oldRoot := ociConfig.Root.Path
reconcileOciConfig(&ociConfig, commonCfg, hostCfg)
// save
iSulad := transform.GetIsuladCfgTool()
err = iSulad.SaveConfig(id, &ociConfig, iSulad.MarshalIndent, iSulad.GetOciConfigPath)
if err != nil {
logrus.Errorf("save v2 config to file %s failed", iSulad.GetOciConfigPath(id))
return nil, "", errors.Wrap(err, "save config.json")
}
return &ociConfig, oldRoot, nil
}
func (t *dockerTransformer) initStorageDriver() (transform.StorageDriver, error) {
isulad := transform.GetIsuladCfgTool()
switch isulad.StorageType() {
case transform.Overlay2:
return newOverlayDriver(isulad.BaseStorageDriver()), nil
case transform.DeviceMapper:
return newDeviceMapperDriver(isulad.BaseStorageDriver(), t.client), nil
default:
}
return nil, fmt.Errorf("unsupported storage driver type: %s", isulad.StorageType())
}
func (t *dockerTransformer) initContainers() error {
t.ctrs = &sync.Map{}
runningCtrsRoot := filepath.Join(t.StateRoot, "containerd/daemon", containerdRuntime, containerdNameSpace)
infos, err := ioutil.ReadDir(runningCtrsRoot)
if err != nil {
return errors.Wrap(err, "init docker container store failed")
}
for _, info := range infos {
if info.IsDir() && len(info.Name()) == containerIDLen {
t.ctrs.Store(info.Name(), false)
}
}
return nil
}
func (t *dockerTransformer) matchID(id string) (fullID string, status containerStatus) {
t.ctrs.Range(func(k, v interface{}) bool {
fullID, status = "", notExist
tmpID, _ := k.(string)
transformed, _ := v.(bool)
if strings.HasPrefix(tmpID, id) {
if !transformed {
t.ctrs.Store(k, true)
status = needTransform
} else {
status = hasBeenTransformed
}
fullID = tmpID
return false
}
return true
})
return
}
func (t *dockerTransformer) handleSignal() context.Context {
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
ctx, cancel := context.WithCancel(context.Background())
go func() {
if s, ok := <-sigCh; ok {
logrus.Infof("catch signal %v", s)
cancel()
return
}
}()
return ctx
}
/*
* Copyright (c) 2020 Huawei Technologies Co., Ltd.
* isula-transform is licensed under 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.
* Create: 2020-04-24
*/
package docker
import (
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"sort"
"sync"
"testing"
"time"
"github.com/docker/docker/api/types/container"
. "github.com/golang/mock/gomock"
. "github.com/google/go-cmp/cmp"
"github.com/opencontainers/runtime-spec/specs-go"
. "github.com/smartystreets/goconvey/convey"
"github.com/urfave/cli"
"isula.org/isula-transform/transform"
"isula.org/isula-transform/types"
)
const (
transformTestCtrID = "511e7f915e3f5dc09b36a49657125eea4b36a05f862ab3dd01e0b9b2"
notExistCtrID = "notexist"
incorrectCtrID = "incorrectformat"
incorrectFile = "incorrect.json"
)
func TestNew(t *testing.T) {
applyFlags := func(s *flag.FlagSet, flags ...cli.StringFlag) {
for _, f := range flags {
f.Apply(s)
}
}
graphFlag := cli.StringFlag{Name: "docker-graph"}
stateFlag := cli.StringFlag{Name: "docker-state"}
Convey("TestNew", t, func() {
Convey("default config", func() {
flags := flag.NewFlagSet("", flag.ContinueOnError)
applyFlags(flags, graphFlag, stateFlag)
ctx := cli.NewContext(nil, flags, nil)
got := New(ctx)
expect := &dockerTransformer{
BaseTransformer: transform.BaseTransformer{
Name: "docker",
StateRoot: "/var/run/docker",
GraphRoot: "/var/lib/docker",
},
}
So(reflect.DeepEqual(got, expect), ShouldBeTrue)
})
Convey("user defined", func() {
graphFlag.Value = "/test/lib/docker"
stateFlag.Value = "/test/run/docker"
flags := flag.NewFlagSet("", flag.ContinueOnError)
applyFlags(flags, graphFlag, stateFlag)
ctx := cli.NewContext(nil, flags, nil)
got := New(ctx)
expect := &dockerTransformer{
BaseTransformer: transform.BaseTransformer{
Name: "docker",
StateRoot: "/test/run/docker",
GraphRoot: "/test/lib/docker",
},
}
So(reflect.DeepEqual(got, expect), ShouldBeTrue)
})
})
}
func Test_dockerConfigEngine_transformHostConfig(t *testing.T) {
// init
hostCfgFile := types.Hostconfig
tmpdir, err := ioutil.TempDir("", "isula-transform")
if err != nil {
t.Skipf("make temp dir: %v", err)
}
defer os.RemoveAll(tmpdir)
if err := initDockerTransformTest(tmpdir, transformTestCtrID, hostCfgFile, hostCfgFile, true); err != nil {
t.Skipf("init test transform HostConfig failed: %v", err)
}
dt := getTestDockerTransformer(tmpdir)
Convey("Test_dockerConfigEngine_transformHostConfig", t, func() {
Convey("container not exist", func() {
_, _, err := dt.transformHostConfig(notExistCtrID)
So(err.Error(), ShouldContainSubstring, "no such file or directory")
})
Convey("incorrect json format", func() {
if err := initDockerTransformTest(tmpdir, incorrectCtrID,
incorrectFile, hostCfgFile, false); err != nil {
t.Skipf("prepare test incorrect json format failed: %v", err)
}
_, _, err := dt.transformHostConfig(incorrectCtrID)
So(err.Error(), ShouldContainSubstring, "invalid character")
})
Convey("transform successfully", func() {
hGot, lGot, err := dt.transformHostConfig(transformTestCtrID)
hExpect := &types.IsuladHostConfig{
NetworkMode: "host",
Runtime: "lcr",
IpcMode: "shareable",
RestartPolicy: &types.RestartPolicy{
Name: "always",
MaximumRetryCount: 0,
},
ShmSize: 67108864,
}
lExpect := &container.LogConfig{
Type: "json-file",
Config: map[string]string{
"max-size": "30KB",
},
}
So(err, ShouldBeNil)
So(Diff(hExpect, hGot), ShouldBeBlank)
So(Diff(lExpect, lGot), ShouldBeBlank)
})
})
}
//go:generate mockgen -destination mock_storage.go -package docker isula.org/isula-transform/transform StorageDriver
func Test_dockerConfigEngine_transformV2Config(t *testing.T) {
// init
configV2File := "config.v2.json"
tmpdir, err := ioutil.TempDir("", "isula-transform")
if err != nil {
t.Skipf("make temp dir: %v", err)
}
defer os.RemoveAll(tmpdir)
if err := initDockerTransformTest(tmpdir, transformTestCtrID, configV2File, configV2File, true); err != nil {
t.Skipf("init test transform HostConfig failed: %v", err)
}
dt := getTestDockerTransformer(tmpdir)
// mock StorageDriver
ctrl := NewController(t)
defer ctrl.Finish()
sd := NewMockStorageDriver(ctrl)
InOrder(
sd.EXPECT().GenerateRootFs(Any(), Any()).Return("", fmt.Errorf("mock generate failed")),
sd.EXPECT().GenerateRootFs(Any(), Any()).Return("newRootFS", nil),
)
dt.sd = sd
Convey("Test_dockerConfigEngine_transformV2Config", t, func() {
Convey("container not exist", func() {
_, err := dt.transformV2Config(notExistCtrID)
So(err.Error(), ShouldContainSubstring, "no such file or directory")
})
Convey("incorrect json format", func() {
if err := initDockerTransformTest(tmpdir, incorrectCtrID, incorrectFile, configV2File, false); err != nil {
t.Skipf("prepare test incorrect json format failed: %v", err)
}
_, err := dt.transformV2Config(incorrectCtrID)
So(err.Error(), ShouldContainSubstring, "invalid character")
})
Convey("load successfully", func() {
Convey("generate rootfs failed", func() {
_, err := dt.transformV2Config(transformTestCtrID)
So(err.Error(), ShouldContainSubstring, "mock generate failed")
})
Convey("transform successfully", func() {
opts := []v2ConfigReconcileOpt{
v2ConfigWithLogConfig(nil, ""),
}
got, err := dt.transformV2Config(transformTestCtrID, opts...)
expect := &types.IsuladV2Config{
CommonConfig: &types.CommonConfig{
Path: "bash",
Args: []string{},
Config: &types.ContainerCfg{
Tty: true,
OpenStdin: true,
Hostname: "localhost.localdomain",
Env: []string{
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
},
Cmd: []string{
"bash",
},
Labels: make(map[string]string),
Image: "isulatransformtestcontainer:image",
Annotations: map[string]string{
"cgroup.dir": "/isulad",
"log.console.driver": "json-file",
"log.console.file": "none",
"log.console.filerotate": "7",
"log.console.filesize": "30KB",
"native.umask": "secure",
"rootfs.mount": "/var/lib/isulad/mnt/rootfs",
},
},
Created: time.Unix(1579744800, 000000000).Local(),
Image: "isulatransformtestcontainer:image",
ImageType: "oci",
HostnamePath: tmpdir + "/lib/isulad/engines/lcr/511e7f915e3f5dc09b36a49657125eea4b36a05f862ab3dd01e0b9b2/hostname",
HostsPath: tmpdir + "/lib/isulad/engines/lcr/511e7f915e3f5dc09b36a49657125eea4b36a05f862ab3dd01e0b9b2/hosts",
ResolvConfPath: tmpdir + "/lib/isulad/engines/lcr/511e7f915e3f5dc09b36a49657125eea4b36a05f862ab3dd01e0b9b2/resolv.conf",
OriginHostnamePath: "/var/lib/docker/containers/511e7f915e3f5dc09b36a49657125eea4b36a05f862ab3dd01e0b9b2/hostname",
OriginHostsPath: "/var/lib/docker/containers/511e7f915e3f5dc09b36a49657125eea4b36a05f862ab3dd01e0b9b2/hosts",
OriginResolvConfPath: "/var/lib/docker/containers/511e7f915e3f5dc09b36a49657125eea4b36a05f862ab3dd01e0b9b2/resolv.conf",
ShmPath: tmpdir + "/lib/isulad/engines/lcr/511e7f915e3f5dc09b36a49657125eea4b36a05f862ab3dd01e0b9b2/mounts/shm",
LogPath: "none",
LogDriver: "json-file",
BaseFs: "newRootFS",
MountPoints: make(map[string]types.Mount),
Name: "isulatransformtestcontainer",
RestartCount: 0,
ID: "511e7f915e3f5dc09b36a49657125eea4b36a05f862ab3dd01e0b9b2",
HasBeenManuallyStopped: false,
HasBeenStartedBefore: true,
},
Image: "sha256:dc6b3e3cf28225d72351d5dbddc35ea08a08ad83725043903df61448c9e466a0",
State: &types.ContainerState{
Running: false,
Pid: 17373,
StartedAt: time.Unix(1579744800, 000000000).Local(),
},
}
So(err, ShouldBeNil)
// types.ContainerState.FinishedAt use time.Now().Local()
// need to sync from got
expect.State.FinishedAt = got.State.FinishedAt
So(Diff(expect, got), ShouldBeBlank)
})
})
})
}
func Test_dockerConfigEngine_transformOciConfig(t *testing.T) {
ociCfgFile := "config.json"
tmpdir, err := ioutil.TempDir("", "isula-transform")
if err != nil {
t.Skipf("make temp dir: %v", err)
}
defer os.RemoveAll(tmpdir)
if err := initDockerTransformTest(tmpdir, transformTestCtrID, ociCfgFile, ociCfgFile, true); err != nil {
t.Skipf("init test transform HostConfig failed: %v", err)
}
dt := getTestDockerTransformer(tmpdir)
Convey("Test_dockerConfigEngine_transformOciConfig", t, func() {
Convey("container not exist", func() {
_, _, err := dt.transformOciConfig(notExistCtrID, nil, nil)
So(err.Error(), ShouldContainSubstring, "no such file or directory")
})
Convey("incorrect json format", func() {
if err := initDockerTransformTest(tmpdir, incorrectCtrID, incorrectFile, ociCfgFile, false); err != nil {
t.Skipf("prepare test incorrect json format failed: %v", err)
}
_, _, err := dt.transformOciConfig(incorrectCtrID, nil, nil)
So(err.Error(), ShouldContainSubstring, "invalid character")
})
Convey("transform successfully", func() {
var (
basePath = transform.GetIsuladCfgTool().GetRuntimePath() + transformTestCtrID
hostname = basePath + "/hostname"
hosts = basePath + "/hosts"
resolvconf = basePath + "/resolv.conf"
shmPath = basePath + "/mounts/shm"
devicesNum = [][]int64{{5, 2}, {136, -1}}
oldRootFS = "/old/root/fs"
common = &types.CommonConfig{
BaseFs: "/new/root/fs",
Config: &types.ContainerCfg{
Annotations: map[string]string{
"testOci": "testOci",
},
},
HostnamePath: hostname,
ResolvConfPath: resolvconf,
HostsPath: hosts,
ShmPath: shmPath,
ID: transformTestCtrID,
}
host = &types.IsuladHostConfig{
ShmSize: 123456,
}
)
got, gotOldRootFS, err := dt.transformOciConfig(transformTestCtrID, common, host)
expect := &specs.Spec{
Version: "1.0.1-dev",
Process: &specs.Process{
Terminal: true,
User: specs.User{
UID: 0,
GID: 0,
},
Args: []string{"bash"},
Env: []string{
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"HOSTNAME=localhost.localdomain",
"TERM=xterm",
},
Cwd: "/",
},
Root: &specs.Root{
Path: "/new/root/fs",
},
Hostname: "localhost.localdomain",
Mounts: []specs.Mount{
{Destination: "/etc/resolv.conf", Type: "bind", Source: resolvconf, Options: []string{"rbind", "rprivate"}},
{Destination: "/etc/hostname", Type: "bind", Source: hostname, Options: []string{"rbind", "rprivate"}},
{Destination: "/etc/hosts", Type: "bind", Source: hosts, Options: []string{"rbind", "rprivate"}},
{Destination: "/dev/shm", Type: "bind", Source: shmPath, Options: []string{"rbind", "rprivate", "mode=1777", "size=123456"}},
},
Hooks: &specs.Hooks{},
Annotations: map[string]string{
"cgroup.dir": "/isulad",
"native.umask": "secure",
"testOci": "testOci",
},
Linux: &specs.Linux{
Resources: &specs.LinuxResources{
Devices: []specs.LinuxDeviceCgroup{
{Allow: false, Access: "rwm"},
{Allow: true, Type: "c", Major: &devicesNum[0][0], Minor: &devicesNum[0][1], Access: "rwm"},
{Allow: true, Type: "c", Major: &devicesNum[1][0], Minor: &devicesNum[1][1], Access: "rwm"},
},
},
CgroupsPath: "/isulad/" + transformTestCtrID,
Namespaces: []specs.LinuxNamespace{
{Type: "network"},
},
},
}
So(err, ShouldBeNil)
So(gotOldRootFS, ShouldEqual, oldRootFS)
So(Diff(expect, got), ShouldBeBlank)
})
})
}
func Test_dockerTransformer_initStorageDriver(t *testing.T) {
dt := &dockerTransformer{}
Convey("Test_dockerTransformer_initStorageDriver", t, func() {
Convey("overlay2 driver", func() {
err := transform.InitIsuladTool("", "", "overlay2", "")
So(err, ShouldBeNil)
ol, err := dt.initStorageDriver()
So(err, ShouldBeNil)
_, ok := ol.(*overlayDriver)
So(ok, ShouldBeTrue)
})
Convey("devicemapper driver", func() {
err := transform.InitIsuladTool("", "", "devicemapper", "")
So(err, ShouldBeNil)
ol, err := dt.initStorageDriver()
So(err, ShouldBeNil)
_, ok := ol.(*deviceMapperDriver)
So(ok, ShouldBeTrue)
})
})
}
func Test_dockerTransformer_initContainers(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "isula-transform")
if err != nil {
t.Skipf("make temp dir: %v", err)
}
defer os.RemoveAll(tmpdir)
dt := getTestDockerTransformer(tmpdir)
err = os.MkdirAll(dt.StateRoot+"/containerd/daemon/"+containerdRuntime, 0700)
if err != nil {
t.Skipf("make docker graph dir: %v", err)
}
Convey("Test_dockerTransformer_initContainers", t, func() {
ctrsRoot := filepath.Join(dt.StateRoot, "containerd/daemon", containerdRuntime, containerdNameSpace)
Convey("containers is not directory", func() {
_, err = os.OpenFile(ctrsRoot, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0640)
if err != nil {
t.Skipf("create graph root contains: %v", err)
}
defer os.RemoveAll(ctrsRoot)
initErr := dt.initContainers()
So(initErr, ShouldBeError)
So(initErr.Error(), ShouldContainSubstring, "init docker container store failed")
})
Convey("containers contains item invalid", func() {
err = os.MkdirAll(ctrsRoot, 0700)
if err != nil {
t.Skipf("make docker containers dir: %v", err)
}
defer os.RemoveAll(ctrsRoot)
ctrs := []string{
"ebe35e6089fa868bfde477bb2cc749a88b1e93dc66fa199b0ed6927f10f86b5a",
"d77fdb420e801f1e9cf081b5bd5f2948047e5a7a790d12a81edd8f4c0f4fef4d",
"lessthen64",
}
for _, ctr := range ctrs {
err := os.MkdirAll(ctrsRoot+"/"+ctr, 0700)
if err != nil {
t.Skipf("make running container %s dir: %v", ctr, err)
}
}
fileCtr := ctrsRoot + "/" + "d32d678c960aeb50d55107ed1aaf0c82957a88c431d422657a5df6a6920f0446"
if _, err := os.OpenFile(fileCtr, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0640); err != nil {
t.Skipf("create graph root contains: %v", err)
}
initErr := dt.initContainers()
So(initErr, ShouldBeNil)
var loadCtrs []string
dt.ctrs.Range(func(key, value interface{}) bool {
ctr, _ := key.(string)
loadCtrs = append(loadCtrs, ctr)
return true
})
expectCtrs := ctrs[0:2]
sort.StringSlice(loadCtrs).Sort()
sort.StringSlice(expectCtrs).Sort()
So(Diff(loadCtrs, expectCtrs), ShouldBeBlank)
})
})
}
func Test_dockerTransformer_matchID(t *testing.T) {
Convey("Test_dockerTransformer_matchID", t, func() {
dt := &dockerTransformer{
ctrs: &sync.Map{},
}
testCtrs := []string{
"ebe35e6089fa868bfde477bb2cc749a88b1e93dc66fa199b0ed6927f10f86b5a",
"d77fdb420e801f1e9cf081b5bd5f2948047e5a7a790d12a81edd8f4c0f4fef4d",
"8af0fdebdceb85143394db47d145a3161038ab8b723583fd5f3c8d39470a3017",
}
for _, ctr := range testCtrs {
dt.ctrs.Store(ctr, false)
}
Convey("not exist", func() {
id, st := dt.matchID("notexist")
So(id, ShouldBeBlank)
So(st, ShouldEqual, notExist)
})
Convey("update transform status", func() {
prefix := "d77"
id, st := dt.matchID(prefix)
So(id, ShouldEqual, "d77fdb420e801f1e9cf081b5bd5f2948047e5a7a790d12a81edd8f4c0f4fef4d")
So(st, ShouldEqual, needTransform)
id, st = dt.matchID(prefix)
So(id, ShouldEqual, "d77fdb420e801f1e9cf081b5bd5f2948047e5a7a790d12a81edd8f4c0f4fef4d")
So(st, ShouldEqual, hasBeenTransformed)
})
})
}
func loadTestData(name string) ([]byte, error) {
return ioutil.ReadFile("./testdata/" + name)
}
func initDockerTransformTest(repalceVar, id, src, dest string, initIsuladTool bool) error {
var fileRoot string
switch dest {
case types.V2config, types.Hostconfig:
fileRoot = repalceVar + "/lib/docker/containers/" + id
case types.Ociconfig:
fileRoot = repalceVar + "/run/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/" + id
}
if err := os.MkdirAll(fileRoot, 0700); err != nil {
return err
}
testData, err := loadTestData(src)
if err != nil {
return err
}
if err := ioutil.WriteFile(fileRoot+"/"+dest, testData, 0640); err != nil {
return err
}
if initIsuladTool {
graph := filepath.Join(repalceVar + "/lib/isulad")
_ = transform.InitIsuladTool(graph, "", "", "")
return transform.GetIsuladCfgTool().PrepareBundleDir(id)
}
return nil
}
func getTestDockerTransformer(tmpdir string) *dockerTransformer {
return &dockerTransformer{
BaseTransformer: transform.BaseTransformer{
Name: "docker",
StateRoot: tmpdir + "/run/docker",
GraphRoot: tmpdir + "/lib/docker",
},
}
}
/*
* Copyright (c) 2020 Huawei Technologies Co., Ltd.
* isula-transform is licensed under 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.
* Create: 2020-04-24
*/
package docker
import (
"os/exec"
"strings"
"isula.org/isula-transform/transform"
"isula.org/isula-transform/types"
)
type overlayDriver struct {
transform.BaseStorageDriver
}
func newOverlayDriver(base transform.BaseStorageDriver) transform.StorageDriver {
return &overlayDriver{base}
}
func (od *overlayDriver) GenerateRootFs(id, image string) (string, error) {
return od.BaseStorageDriver.GenerateRootFs(id, image)
}
// only copy diff from old to new
func (od *overlayDriver) TransformRWLayer(ctr *types.IsuladV2Config, oldRootFs string) error {
srcRoot := strings.TrimSuffix(oldRootFs, "/merged")
destRoot := strings.TrimSuffix(ctr.CommonConfig.BaseFs, "/merged")
if err := exec.Command("cp", "-ra", srcRoot+"/diff", destRoot).Run(); err != nil {
return err
}
return nil
}
func (od *overlayDriver) Cleanup(id string) {
od.BaseStorageDriver.CleanupRootFs(id)
}
/*
* Copyright (c) 2020 Huawei Technologies Co., Ltd.
* isula-transform is licensed under 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.
* Create: 2020-04-24
*/
package docker
import (
"path/filepath"
"regexp"
"strconv"
"strings"
"time"
"github.com/docker/docker/api/types/container"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
"isula.org/isula-transform/types"
)
const (
logDriverJSONFile = "json-file"
logDriverSyslog = "syslog"
defaultLogSize = "30KB"
defaultLogRotate = "7"
defaultLogDriver = logDriverJSONFile
defaultLogPath = "none"
defaultCgroupDir = "/isulad"
minValidImagePartsLen = 2
)
type v2ConfigReconcileOpt func(*types.IsuladV2Config)
func v2ConfigWithImage(image string) v2ConfigReconcileOpt {
return func(v2 *types.IsuladV2Config) {
v2.CommonConfig.Image = imageRemoveSuffixDigest(image)
v2.CommonConfig.ImageType = "oci"
}
}
func v2ConfigWithCgroupParent(cgroupParent string) v2ConfigReconcileOpt {
return func(v2 *types.IsuladV2Config) {
var cgroupDir string
if strings.HasPrefix(cgroupParent, "/docker/") || !strings.HasSuffix(cgroupParent, "/"+v2.CommonConfig.ID) {
cgroupDir = defaultCgroupDir
} else {
cgroupDir = strings.TrimSuffix(cgroupParent, "/"+v2.CommonConfig.ID)
}
v2.CommonConfig.Config.Annotations["cgroup.dir"] = cgroupDir
}
}
func v2ConfigWithOomScoreAdj(oomScore int) v2ConfigReconcileOpt {
return func(v2 *types.IsuladV2Config) {
v2.CommonConfig.Config.Annotations["proc.oom_score_adj"] = strconv.Itoa(oomScore)
}
}
func v2ConfigWithFilesLimit(filesLimit int64) v2ConfigReconcileOpt {
return func(v2 *types.IsuladV2Config) {
v2.CommonConfig.Config.Annotations["files.limit"] = strconv.FormatInt(filesLimit, 10)
}
}
func v2ConfigWithLogConfig(cfg *container.LogConfig, basePath string) v2ConfigReconcileOpt {
return func(v2 *types.IsuladV2Config) {
if cfg == nil {
cfg = &container.LogConfig{
Type: "default",
}
}
switch cfg.Type {
case logDriverJSONFile:
// docker allowed logopts:
// max-file、max-size、compress、labels、env、env-regex、tag
// isulad only support:
// max-file, max-size
v2.CommonConfig.LogDriver = logDriverJSONFile
v2.CommonConfig.Config.Annotations["log.console.driver"] = logDriverJSONFile
v2.CommonConfig.LogPath = filepath.Join(basePath, "console.log")
v2.CommonConfig.Config.Annotations["log.console.file"] = v2.CommonConfig.LogPath
if rotate, exist := cfg.Config["max-file"]; exist {
v2.CommonConfig.Config.Annotations["log.console.filerotate"] = rotate
} else {
v2.CommonConfig.Config.Annotations["log.console.filerotate"] = defaultLogRotate
}
if size, exist := cfg.Config["max-size"]; exist {
v2.CommonConfig.Config.Annotations["log.console.filesize"] = size
} else {
v2.CommonConfig.Config.Annotations["log.console.filesize"] = defaultLogSize
}
case logDriverSyslog:
v2.CommonConfig.LogDriver = logDriverSyslog
v2.CommonConfig.Config.Annotations["log.console.driver"] = logDriverSyslog
// docker allowed LogOpts:
// env, env-regex, labels, syslog-facility, tag, syslog-format
// syslog-address, syslog-tls-ca-cert, syslog-tls-cert, syslog-tls-key, syslog-tls-skip-verify
// isulad only support:
// tag, facility
if tag, exist := cfg.Config["tag"]; exist {
v2.CommonConfig.Config.Annotations["log.console.tag"] = tag
}
if facility, exist := cfg.Config["syslog-facility"]; exist {
v2.CommonConfig.Config.Annotations["log.console.facility"] = facility
}
default:
// use isulad default driver without file
v2.CommonConfig.LogDriver = defaultLogDriver
v2.CommonConfig.LogPath = defaultLogPath
v2.CommonConfig.Config.Annotations["log.console.driver"] = defaultLogDriver
v2.CommonConfig.Config.Annotations["log.console.file"] = defaultLogPath
v2.CommonConfig.Config.Annotations["log.console.filerotate"] = defaultLogRotate
v2.CommonConfig.Config.Annotations["log.console.filesize"] = defaultLogSize
}
}
}
func genV2OptsFromHostCfg(h *types.IsuladHostConfig) []v2ConfigReconcileOpt {
if h == nil {
return nil
}
var opts []v2ConfigReconcileOpt
if h.OomScoreAdj != 0 {
opts = append(opts, v2ConfigWithOomScoreAdj(h.OomScoreAdj))
}
if h.FilesLimit != 0 {
opts = append(opts, v2ConfigWithFilesLimit(h.FilesLimit))
}
return opts
}
func reconcileV2Config(v2 *types.IsuladV2Config, basePath string, opts ...v2ConfigReconcileOpt) {
for _, o := range opts {
o(v2)
}
// modify hosts hostname and resolv.conf
v2.CommonConfig.OriginHostsPath = v2.CommonConfig.HostsPath
v2.CommonConfig.HostsPath = filepath.Join(basePath, types.Hosts)
v2.CommonConfig.OriginHostnamePath = v2.CommonConfig.HostnamePath
v2.CommonConfig.HostnamePath = filepath.Join(basePath, types.Hostname)
v2.CommonConfig.OriginResolvConfPath = v2.CommonConfig.ResolvConfPath
v2.CommonConfig.ResolvConfPath = filepath.Join(basePath, types.Resolv)
v2.CommonConfig.ShmPath = filepath.Join(basePath, "mounts", "shm")
// add annotations
v2.CommonConfig.Config.Annotations["rootfs.mount"] = "/var/lib/isulad/mnt/rootfs"
// fix time format and update state
v2.CommonConfig.Created = v2.CommonConfig.Created.Local()
v2.State.Paused = false
v2.State.Running = false
v2.State.StartedAt = v2.State.StartedAt.Local()
v2.State.FinishedAt = time.Now().Local()
// fix name prefix
v2.CommonConfig.Name = strings.TrimPrefix(v2.CommonConfig.Name, "/")
}
func imageRemoveSuffixDigest(reference string) string {
reg := regexp.MustCompile(`([a-zA-Z0-9._\-/:]*)(?:@[[:xdigit:]]+)?`)
matchs := reg.FindStringSubmatch(reference)
if len(matchs) < minValidImagePartsLen {
return ""
}
return matchs[1]
}
func reconcileHostConfig(h *types.IsuladHostConfig, runtime string) {
h.Runtime = runtime
if h.RestartPolicy.Name == "unless-stopped" {
logrus.Info("isulad not support unless-stopped policy, transform to always")
h.RestartPolicy.Name = "always"
}
if h.UsernsMode != "" {
logrus.Infof("isulad not allowed share user namespace %s, replace to nil", h.UsernsMode)
h.UsernsMode = ""
}
}
func reconcileOciConfig(s *specs.Spec, c *types.CommonConfig, h *types.IsuladHostConfig) {
// Annotations opt sync with CommonConfig
for k, v := range c.Config.Annotations {
s.Annotations[k] = v
}
// rootFs opt
s.Root.Path = c.BaseFs
// user-defined cgroup path
if _, exist := s.Annotations["cgroup.dir"]; !exist {
s.Annotations["cgroup.dir"] = defaultCgroupDir
}
cgroupDir := s.Annotations["cgroup.dir"]
s.Linux.CgroupsPath = filepath.Join(cgroupDir, c.ID)
// set linux namespace path
// pid, ipc and network might be container mode
for idx := range s.Linux.Namespaces {
s.Linux.Namespaces[idx].Path = ociAdaptSharedNamespaceContainer(s.Linux.Namespaces[idx], h)
}
// when privileged, there is no need to create pty device
if !h.Privileged {
ociAddMustDevice(s)
}
// mounts opt
for idx := range s.Mounts {
var source string
switch s.Mounts[idx].Destination {
case "/etc/hostname":
source = c.HostnamePath
case "/etc/resolv.conf":
source = c.ResolvConfPath
case "/etc/hosts":
source = c.HostsPath
case "/dev/shm":
source = c.ShmPath
shmModeOpt := "mode=1777"
shmSizeOpt := "size=" + strconv.FormatInt(h.ShmSize, 10)
s.Mounts[idx].Options = append(s.Mounts[idx].Options, shmModeOpt, shmSizeOpt)
default:
continue
}
s.Mounts[idx].Source = source
}
// when device.Path contains ":", lxc does not support, remove them
end := 0
for _, device := range s.Linux.Devices {
if !strings.Contains(device.Path, ":") {
s.Linux.Devices[end] = device
end++
}
}
s.Linux.Devices = s.Linux.Devices[:end]
}
func ociAdaptSharedNamespaceContainer(ns specs.LinuxNamespace, h *types.IsuladHostConfig) string {
isContainer := func(mode string) (string, bool) {
parts := strings.SplitN(mode, ":", 2)
if len(parts) > 1 && parts[0] == "container" && len(parts[1]) == containerIDLen {
return parts[1], true
}
return "", false
}
switch ns.Type {
case specs.IPCNamespace:
if ipcCtr, ok := isContainer(h.IpcMode); ok {
return ipcCtr
}
case specs.PIDNamespace:
if pidCtr, ok := isContainer(h.PidMode); ok {
return pidCtr
}
case specs.NetworkNamespace:
if netCtr, ok := isContainer(h.NetworkMode); ok {
return netCtr
}
default:
}
return ""
}
func ociAddMustDevice(spec *specs.Spec) {
addDeviceFunc := func(major, minor int64) {
dev := specs.LinuxDeviceCgroup{
Type: "c",
Allow: true,
Major: &major,
Minor: &minor,
Access: "rwm",
}
for _, item := range spec.Linux.Resources.Devices {
if (item.Major != nil && *dev.Major == *item.Major) &&
(item.Minor != nil && *dev.Minor == *item.Minor) {
return
}
}
spec.Linux.Resources.Devices = append(spec.Linux.Resources.Devices, dev)
}
// ptmx: PTY master multiplex
mustDevices := []struct {
Major int64
Minor int64
}{
{
Major: 5,
Minor: 2,
},
{
Major: 136,
Minor: -1,
},
}
for _, dev := range mustDevices {
addDeviceFunc(dev.Major, dev.Minor)
}
}
/*
* Copyright (c) 2020 Huawei Technologies Co., Ltd.
* isula-transform is licensed under 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.
* Create: 2020-04-24
*/
package docker
import (
"strconv"
"testing"
"time"
"github.com/docker/docker/api/types/container"
. "github.com/google/go-cmp/cmp"
"github.com/opencontainers/runtime-spec/specs-go"
. "github.com/smartystreets/goconvey/convey"
"isula.org/isula-transform/types"
)
const (
reconcileTestCtrID = "isulatransformreconciletestcontainer"
reconcileTestConnectCtrID = "1234567890123456789012345678901234567890123456789012345678901234"
reconcileTestImage = "isulatransformreconciletestcontainer:image"
startUnixTimeStamp int64 = 1579744800
)
func Test_reconcileV2Config(t *testing.T) {
Convey("Test_reconcileV2Config", t, func() {
baseDockerPath := "/var/lib/docker/containers/" + reconcileTestCtrID
baseLcrPath := "/var/lib/isulad/engines/lcr/" + reconcileTestCtrID
baseCfg := &types.IsuladV2Config{
CommonConfig: &types.CommonConfig{
Name: "/test_container",
HostnamePath: baseDockerPath + "/hostname",
HostsPath: baseDockerPath + "/hosts",
ResolvConfPath: baseDockerPath + "/resolv.conf",
Config: &types.ContainerCfg{
Annotations: make(map[string]string),
},
Created: time.Unix(startUnixTimeStamp, 000000000),
ID: reconcileTestCtrID,
},
State: &types.ContainerState{
Running: true,
StartedAt: time.Unix(startUnixTimeStamp, 000000000),
},
}
expectCfg := &types.IsuladV2Config{
CommonConfig: &types.CommonConfig{
Name: "test_container",
OriginHostnamePath: baseDockerPath + "/hostname",
OriginHostsPath: baseDockerPath + "/hosts",
OriginResolvConfPath: baseDockerPath + "/resolv.conf",
HostnamePath: baseLcrPath + "/hostname",
HostsPath: baseLcrPath + "/hosts",
ResolvConfPath: baseLcrPath + "/resolv.conf",
ShmPath: baseLcrPath + "/mounts/shm",
Config: &types.ContainerCfg{
Annotations: map[string]string{
"rootfs.mount": "/var/lib/isulad/mnt/rootfs",
},
},
Created: time.Unix(startUnixTimeStamp, 000000000).Local(),
ID: reconcileTestCtrID,
},
State: &types.ContainerState{
Running: false,
StartedAt: time.Unix(startUnixTimeStamp, 000000000).Local(),
},
}
Convey("reconcile image", func() {
expectCfg.CommonConfig.Image = reconcileTestImage
expectCfg.CommonConfig.ImageType = "oci"
opts := []v2ConfigReconcileOpt{
v2ConfigWithImage(reconcileTestImage + "@sha256:b41eab535e9ce1239966da529a85e624d117f4d2f8911827d0d7c1e93e92a3e3"),
}
reconcileV2Config(baseCfg, baseLcrPath, opts...)
// types.ContainerState.FinishedAt use time.Now().Local()
// need to sync from base
expectCfg.State.FinishedAt = baseCfg.State.FinishedAt
So(Diff(baseCfg, expectCfg), ShouldBeBlank)
})
Convey("docker CgroupParent", func() {
originCgroupParent := "/docker/" + reconcileTestCtrID
expectCfg.CommonConfig.Config.Annotations["cgroup.dir"] = "/isulad"
opts := []v2ConfigReconcileOpt{
v2ConfigWithCgroupParent(originCgroupParent),
}
reconcileV2Config(baseCfg, baseLcrPath, opts...)
expectCfg.State.FinishedAt = baseCfg.State.FinishedAt
So(Diff(baseCfg, expectCfg), ShouldBeBlank)
})
Convey("user CgroupParent", func() {
originCgroupParent := "/test/" + reconcileTestCtrID
expectCfg.CommonConfig.Config.Annotations["cgroup.dir"] = "/test"
opts := []v2ConfigReconcileOpt{
v2ConfigWithCgroupParent(originCgroupParent),
}
reconcileV2Config(baseCfg, baseLcrPath, opts...)
expectCfg.State.FinishedAt = baseCfg.State.FinishedAt
So(Diff(baseCfg, expectCfg), ShouldBeBlank)
})
Convey("json-file log driver", func() {
expectCfg.CommonConfig.LogDriver = logDriverJSONFile
expectCfg.CommonConfig.LogPath = baseLcrPath + "/console.log"
expectCfg.CommonConfig.Config.Annotations["log.console.driver"] = logDriverJSONFile
expectCfg.CommonConfig.Config.Annotations["log.console.file"] = baseLcrPath + "/console.log"
Convey("user define", func() {
logConfig := &container.LogConfig{
Type: logDriverJSONFile,
Config: map[string]string{
"max-file": "3",
"max-size": "10M",
"env": "not retain",
},
}
expectCfg.CommonConfig.Config.Annotations["log.console.filerotate"] = "3"
expectCfg.CommonConfig.Config.Annotations["log.console.filesize"] = "10M"
opts := []v2ConfigReconcileOpt{
v2ConfigWithLogConfig(logConfig, baseLcrPath),
}
reconcileV2Config(baseCfg, baseLcrPath, opts...)
expectCfg.State.FinishedAt = baseCfg.State.FinishedAt
So(Diff(baseCfg, expectCfg), ShouldBeBlank)
})
Convey("use default", func() {
logConfig := &container.LogConfig{
Type: "json-file",
Config: map[string]string{},
}
expectCfg.CommonConfig.Config.Annotations["log.console.filerotate"] = "7"
expectCfg.CommonConfig.Config.Annotations["log.console.filesize"] = defaultLogSize
opts := []v2ConfigReconcileOpt{
v2ConfigWithLogConfig(logConfig, baseLcrPath),
}
reconcileV2Config(baseCfg, baseLcrPath, opts...)
expectCfg.State.FinishedAt = baseCfg.State.FinishedAt
So(Diff(baseCfg, expectCfg), ShouldBeBlank)
})
})
Convey("syslog log driver", func() {
logConfig := &container.LogConfig{
Type: "syslog",
Config: map[string]string{
"tag": "test",
"syslog-facility": "local1",
"env": "not retain",
},
}
expectCfg.CommonConfig.LogDriver = logDriverSyslog
expectCfg.CommonConfig.Config.Annotations["log.console.driver"] = logDriverSyslog
expectCfg.CommonConfig.Config.Annotations["log.console.tag"] = "test"
expectCfg.CommonConfig.Config.Annotations["log.console.facility"] = "local1"
opts := []v2ConfigReconcileOpt{
v2ConfigWithLogConfig(logConfig, baseLcrPath),
}
reconcileV2Config(baseCfg, baseLcrPath, opts...)
expectCfg.State.FinishedAt = baseCfg.State.FinishedAt
So(Diff(baseCfg, expectCfg), ShouldBeBlank)
})
Convey("not support log driver", func() {
logConfig := &container.LogConfig{
Type: "journald",
Config: map[string]string{
"tag": "test",
},
}
expectCfg.CommonConfig.LogDriver = defaultLogDriver
expectCfg.CommonConfig.LogPath = defaultLogPath
expectCfg.CommonConfig.Config.Annotations["log.console.driver"] = defaultLogDriver
expectCfg.CommonConfig.Config.Annotations["log.console.file"] = defaultLogPath
expectCfg.CommonConfig.Config.Annotations["log.console.filerotate"] = "7"
expectCfg.CommonConfig.Config.Annotations["log.console.filesize"] = defaultLogSize
opts := []v2ConfigReconcileOpt{
v2ConfigWithLogConfig(logConfig, baseLcrPath),
}
reconcileV2Config(baseCfg, baseLcrPath, opts...)
expectCfg.State.FinishedAt = baseCfg.State.FinishedAt
So(Diff(baseCfg, expectCfg), ShouldBeBlank)
})
Convey("oom score adj", func() {
oomScore := 100
expectCfg.CommonConfig.Config.Annotations["proc.oom_score_adj"] = "100"
opts := []v2ConfigReconcileOpt{
v2ConfigWithOomScoreAdj(oomScore),
}
reconcileV2Config(baseCfg, baseLcrPath, opts...)
expectCfg.State.FinishedAt = baseCfg.State.FinishedAt
So(Diff(baseCfg, expectCfg), ShouldBeBlank)
})
})
}
func Test_imageRemoveSuffixDigest(t *testing.T) {
var (
baseImage = "ubuntu"
imgTag = ":18.04"
imgDigest = "@sha256:dc6b3e3cf28225d72351d5dbddc35ea08a08ad83725043903df61448c9e466a0"
withTag = baseImage + imgTag
withDigest = baseImage + imgDigest
fullReference = baseImage + imgTag + imgDigest
invalidImage = "*&"
)
Convey("Test_imageRemoveSuffixDigest", t, func() {
Convey("with tag only", func() {
So(imageRemoveSuffixDigest(withTag), ShouldEqual, withTag)
})
Convey("with digest only", func() {
So(imageRemoveSuffixDigest(withDigest), ShouldEqual, baseImage)
})
Convey("full reference", func() {
So(imageRemoveSuffixDigest(fullReference), ShouldEqual, withTag)
})
Convey("invalid reference", func() {
So(imageRemoveSuffixDigest(invalidImage), ShouldBeBlank)
})
})
}
func Test_reconcileHostConfig(t *testing.T) {
Convey("Test_reconcileHostConfig", t, func() {
runtime := "lcr"
h := &types.IsuladHostConfig{
RestartPolicy: &types.RestartPolicy{
Name: "unless-stopped",
MaximumRetryCount: 0,
},
UsernsMode: "host",
}
reconcileHostConfig(h, runtime)
So(h.Runtime, ShouldEqual, runtime)
So(h.RestartPolicy.Name, ShouldEqual, "always")
So(h.UsernsMode, ShouldEqual, "")
})
}
func Test_reconcileOciConfig(t *testing.T) {
Convey("Test_reconcileOciConfig", t, func() {
var deviceNumber [][]int64 = [][]int64{{5, 2}, {136, -1}}
baseSpec := &specs.Spec{
Annotations: make(map[string]string),
Root: &specs.Root{
Path: "/old/path/of/base/rootfs",
},
Linux: &specs.Linux{
Namespaces: []specs.LinuxNamespace{
{Type: "pid"},
{Type: "network"},
{Type: "ipc"},
{Type: "uts"},
},
Resources: &specs.LinuxResources{
Devices: []specs.LinuxDeviceCgroup{
{Allow: true, Type: "c", Major: &deviceNumber[0][0], Minor: &deviceNumber[0][1], Access: "rwm"},
},
},
CgroupsPath: "/docker/" + reconcileTestCtrID,
Devices: []specs.LinuxDevice{
{Path: "/dev/test/0:0:0:0"},
{Path: "/dev/null"},
},
},
Mounts: []specs.Mount{
{Destination: "/etc/hostname", Source: "/old/path/of/hostname"},
{Destination: "/etc/hosts", Source: "/old/path/of/hosts"},
{Destination: "/etc/resolv.conf", Source: "/old/path/of/resolv.conf"},
{Destination: "/dev/shm", Source: "/old/path/of/mounts/shm"},
{Destination: "/data", Source: "/data"},
},
}
baseCommonCfg := &types.CommonConfig{
ID: reconcileTestCtrID,
BaseFs: "/new/path/of/base/rootfs",
HostnamePath: "/new/path/of/hostname",
HostsPath: "/new/path/of/hosts",
ResolvConfPath: "/new/path/of/resolv.conf",
ShmPath: "/new/path/of/mounts/shm",
Config: &types.ContainerCfg{
Annotations: map[string]string{"testAnnotations": "wuhan is a heroical city"},
},
}
expectSpec := &specs.Spec{
Annotations: map[string]string{
"cgroup.dir": "/isulad",
"testAnnotations": "wuhan is a heroical city",
},
Root: &specs.Root{
Path: "/new/path/of/base/rootfs",
},
Linux: &specs.Linux{
Namespaces: []specs.LinuxNamespace{
{Type: "pid", Path: reconcileTestConnectCtrID},
{Type: "network", Path: ""},
{Type: "ipc", Path: ""},
{Type: "uts", Path: ""},
},
Resources: &specs.LinuxResources{
Devices: []specs.LinuxDeviceCgroup{
{Allow: true, Type: "c", Major: &deviceNumber[0][0], Minor: &deviceNumber[0][1], Access: "rwm"},
{Allow: true, Type: "c", Major: &deviceNumber[1][0], Minor: &deviceNumber[1][1], Access: "rwm"},
},
},
CgroupsPath: "/isulad/" + reconcileTestCtrID,
Devices: []specs.LinuxDevice{
{Path: "/dev/null"},
},
},
Mounts: []specs.Mount{
{Destination: "/etc/hostname", Source: "/new/path/of/hostname"},
{Destination: "/etc/hosts", Source: "/new/path/of/hosts"},
{Destination: "/etc/resolv.conf", Source: "/new/path/of/resolv.conf"},
{Destination: "/dev/shm", Source: "/new/path/of/mounts/shm", Options: []string{"mode=1777", "size=0"}},
{Destination: "/data", Source: "/data"},
},
}
reconcileOciConfig(baseSpec, baseCommonCfg, &types.IsuladHostConfig{
PidMode: "container:" + reconcileTestConnectCtrID,
NetworkMode: "container:lessen64",
IpcMode: "notcontainer:lessen64",
})
So(Diff(baseSpec, expectSpec), ShouldBeBlank)
})
}
func Test_genV2OptsFromHostCfg(t *testing.T) {
Convey("Test_reconcileOciConfig", t, func() {
Convey("nil hostConfig", func() {
opts := genV2OptsFromHostCfg(nil)
So(opts, ShouldBeNil)
})
Convey("test the functionality of generation opts", func() {
v2 := &types.IsuladV2Config{
CommonConfig: &types.CommonConfig{
Config: &types.ContainerCfg{
Annotations: make(map[string]string),
},
},
}
Convey("OomScoreAdj", func() {
h := &types.IsuladHostConfig{
OomScoreAdj: 1000,
}
opts := genV2OptsFromHostCfg(h)
for _, o := range opts {
o(v2)
}
oomScore, exist := v2.CommonConfig.Config.Annotations["proc.oom_score_adj"]
So(exist, ShouldBeTrue)
So(oomScore, ShouldEqual, strconv.Itoa(h.OomScoreAdj))
})
Convey("files limit", func() {
h := &types.IsuladHostConfig{
FilesLimit: 1000,
}
opts := genV2OptsFromHostCfg(h)
for _, o := range opts {
o(v2)
}
oomScore, exist := v2.CommonConfig.Config.Annotations["files.limit"]
So(exist, ShouldBeTrue)
So(oomScore, ShouldEqual, strconv.FormatInt(h.FilesLimit, 10))
})
})
})
}
/*
* Copyright (c) 2020 Huawei Technologies Co., Ltd.
* isula-transform is licensed under 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.
* Create: 2020-05-18
*/
package docker
import (
"context"
"sync"
"sync/atomic"
)
const (
executed int32 = 1
unexecuted int32 = 0
)
type rollbackFunc func()
type rollback struct {
wg *sync.WaitGroup
st *int32
ctx context.Context
rbFuncs []rollbackFunc
closed bool
closeCh chan bool
}
// newRollback returns a rollback instance
func newRollback(ctx context.Context, wg *sync.WaitGroup) *rollback {
initSt := unexecuted
return &rollback{
wg: wg,
ctx: ctx,
st: &initSt,
closeCh: make(chan bool, 1),
}
}
func (rb *rollback) wait() {
rb.wg.Add(1)
go func() {
defer rb.wg.Done()
for {
select {
case <-rb.ctx.Done():
rb.run()
return
case rb.closed = <-rb.closeCh:
return
}
}
}()
}
func (rb *rollback) close() {
rb.closeCh <- true
close(rb.closeCh)
}
func (rb *rollback) register(f rollbackFunc) {
rb.rbFuncs = append(rb.rbFuncs, f)
}
func (rb *rollback) run() {
if rb.closed || atomic.LoadInt32(rb.st) == executed {
return
}
atomic.StoreInt32(rb.st, executed)
for i := len(rb.rbFuncs) - 1; i >= 0; i-- {
rb.rbFuncs[i]()
}
}
/*
* Copyright (c) 2020 Huawei Technologies Co., Ltd.
* isula-transform is licensed under 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.
* Create: 2020-05-18
*/
package docker
import (
"container/list"
"context"
"sync"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func Test_rollback(t *testing.T) {
Convey("Test rollback", t, func() {
wg := new(sync.WaitGroup)
Convey("Test rollback with cancel", func() {
l := list.New()
ctx, cancel := context.WithCancel(context.Background())
rb := newRollback(ctx, wg)
rb.wait()
rb.register(func() {
l.PushBack("first register, last execute")
})
rb.register(func() {
l.PushBack("last register, first execute")
})
cancel()
wg.Wait()
rb.close()
So(l.Len(), ShouldEqual, 2)
So(l.Front().Value, ShouldEqual, "last register, first execute")
l.Remove(l.Front())
So(l.Front().Value, ShouldEqual, "first register, last execute")
l.Remove(l.Front())
})
Convey("Test rollback with close", func() {
l := list.New()
ctx, cancel := context.WithCancel(context.Background())
rb := newRollback(ctx, wg)
rb.wait()
rb.register(func() {
l.PushBack("first register, last execute")
})
rb.register(func() {
l.PushBack("last register, first execute")
})
rb.close()
cancel()
wg.Wait()
So(l.Len(), ShouldEqual, 0)
})
})
}
{
"ociVersion": "1.0.1-dev",
"process": {
"terminal": true,
"user": {
"uid": 0,
"gid": 0
},
"args": [
"bash"
],
"env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"HOSTNAME=localhost.localdomain",
"TERM=xterm"
],
"cwd": "/"
},
"root": {
"path": "/old/root/fs"
},
"hostname": "localhost.localdomain",
"mounts": [
{
"destination": "/etc/resolv.conf",
"type": "bind",
"source": "/var/lib/docker/containers/511e7f915e3f5dc09b36a49657125eea4b36a05f862ab3dd01e0b9b2/resolv.conf",
"options": [
"rbind",
"rprivate"
]
},
{
"destination": "/etc/hostname",
"type": "bind",
"source": "/var/lib/docker/containers/511e7f915e3f5dc09b36a49657125eea4b36a05f862ab3dd01e0b9b2/hostname",
"options": [
"rbind",
"rprivate"
]
},
{
"destination": "/etc/hosts",
"type": "bind",
"source": "/var/lib/docker/containers/511e7f915e3f5dc09b36a49657125eea4b36a05f862ab3dd01e0b9b2/hosts",
"options": [
"rbind",
"rprivate"
]
},
{
"destination": "/dev/shm",
"type": "bind",
"source": "/var/lib/docker/containers/511e7f915e3f5dc09b36a49657125eea4b36a05f862ab3dd01e0b9b2/mounts/shm",
"options": [
"rbind",
"rprivate"
]
}
],
"hooks": {},
"annotations": {
"native.umask": "secure"
},
"linux": {
"resources": {
"devices": [
{
"allow": false,
"access": "rwm"
}
]
},
"cgroupsPath": "/docker/511e7f915e3f5dc09b36a49657125eea4b36a05f862ab3dd01e0b9b2",
"namespaces": [
{
"type": "network",
"path": "/var/run/docker/netns/default"
}
]
}
}
\ No newline at end of file
{
"StreamConfig": {},
"State": {
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"RemovalInProgress": false,
"Dead": false,
"Pid": 17373,
"ExitCode": 0,
"Error": "",
"StartedAt": "2020-01-23T02:00:00.000000000Z",
"FinishedAt": "2020-04-07T16:00:00.000000000Z",
"Health": null
},
"ID": "511e7f915e3f5dc09b36a49657125eea4b36a05f862ab3dd01e0b9b2",
"Created": "2020-01-23T02:00:00.000000000Z",
"Managed": false,
"Path": "bash",
"Args": [],
"Config": {
"Hostname": "localhost.localdomain",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": true,
"OpenStdin": true,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"bash"
],
"Image": "isulatransformtestcontainer:image",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {},
"Annotations": {
"native.umask": "secure"
}
},
"Image": "sha256:dc6b3e3cf28225d72351d5dbddc35ea08a08ad83725043903df61448c9e466a0",
"NetworkSettings": {
"Bridge": "",
"SandboxID": "",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Networks": {},
"Service": null,
"Ports": {},
"SandboxKey": "/var/run/docker/netns/default",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"IsAnonymousEndpoint": true,
"HasSwarmEndpoint": false
},
"LogPath": "/var/lib/docker/containers/511e7f915e3f5dc09b36a49657125eea4b36a05f862ab3dd01e0b9b2/511e7f915e3f5dc09b36a49657125eea4b36a05f862ab3dd01e0b9b2-json.log",
"Name": "/isulatransformtestcontainer",
"Driver": "overlay2",
"OS": "linux",
"MountLabel": "",
"ProcessLabel": "",
"RestartCount": 0,
"HasBeenStartedBefore": true,
"HasBeenManuallyStopped": false,
"MountPoints": {},
"SecretReferences": null,
"ConfigReferences": null,
"Hooks": {},
"CgroupParent": "/docker/511e7f915e3f5dc09b36a49657125eea4b36a05f862ab3dd01e0b9b2",
"AppArmorProfile": "",
"HostnamePath": "/var/lib/docker/containers/511e7f915e3f5dc09b36a49657125eea4b36a05f862ab3dd01e0b9b2/hostname",
"HostsPath": "/var/lib/docker/containers/511e7f915e3f5dc09b36a49657125eea4b36a05f862ab3dd01e0b9b2/hosts",
"ShmPath": "/var/lib/docker/containers/511e7f915e3f5dc09b36a49657125eea4b36a05f862ab3dd01e0b9b2/mounts/shm",
"ResolvConfPath": "/var/lib/docker/containers/511e7f915e3f5dc09b36a49657125eea4b36a05f862ab3dd01e0b9b2/resolv.conf",
"SeccompProfile": "",
"NoNewPrivileges": false,
"SaveFlag": 0
}
\ No newline at end of file
{
"Binds": null,
"ContainerIDFile": "",
"LogConfig": {
"Type": "json-file",
"Config": {
"max-size": "30KB"
}
},
"NetworkMode": "host",
"PortBindings": {},
"RestartPolicy": {
"Name": "unless-stopped",
"MaximumRetryCount": 0
},
"AutoRemove": false,
"VolumeDriver": "",
"VolumesFrom": null,
"CapAdd": null,
"CapDrop": null,
"Dns": null,
"DnsOptions": null,
"DnsSearch": null,
"ExtraHosts": null,
"GroupAdd": null,
"IpcMode": "shareable",
"Cgroup": "",
"Links": null,
"OomScoreAdj": 0,
"PidMode": "",
"Privileged": false,
"PublishAllPorts": false,
"ReadonlyRootfs": false,
"SecurityOpt": null,
"UTSMode": "",
"UsernsMode": "",
"ShmSize": 67108864,
"Runtime": "runc",
"ConsoleSize": [
0,
0
],
"Isolation": "",
"HookSpec": "",
"CpuShares": 0,
"Memory": 0,
"NanoCpus": 0,
"CgroupParent": "",
"BlkioWeight": 0,
"BlkioWeightDevice": null,
"BlkioDeviceReadBps": null,
"BlkioDeviceWriteBps": null,
"BlkioDeviceReadIOps": null,
"BlkioDeviceWriteIOps": null,
"CpuPeriod": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
"Devices": null,
"DeviceCgroupRules": null,
"DiskQuota": 0,
"KernelMemory": 0,
"MemoryReservation": 0,
"MemorySwap": 0,
"MemorySwappiness": null,
"OomKillDisable": false,
"PidsLimit": 0,
"FilesLimit": 0,
"Ulimits": null,
"CpuCount": 0,
"CpuPercent": 0,
"IOMaximumIOps": 0,
"IOMaximumBandwidth": 0,
"Hugetlbs": null,
"MaskedPaths": [
"/proc/acpi",
"/proc/config.gz",
"/proc/cpuirqstat",
"/proc/fdenable",
"/proc/fdstat",
"/proc/fdthreshold",
"/proc/files_panic_enable",
"/proc/iomem_ext",
"/proc/kbox",
"/proc/kcore",
"/proc/keys",
"/proc/latency_stats",
"/proc/livepatch",
"/proc/memstat",
"/proc/net_namespace",
"/proc/oom_extend",
"/proc/sched_debug",
"/proc/scsi",
"/proc/sig_catch",
"/proc/signo",
"/proc/timer_list",
"/proc/timer_stats",
"/sys/firmware"
],
"ReadonlyPaths": [
"/proc/asound",
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger"
]
}
\ No newline at end of file
incorrect json format data
\ No newline at end of file
/*
* Copyright (c) 2020 Huawei Technologies Co., Ltd.
* isula-transform is licensed under 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.
* Create: 2020-04-24
*/
package transform
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"syscall"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
"isula.org/isula-transform/types"
)
const (
rootDirMode os.FileMode = 0750
mountsDirMode os.FileMode = 0700
cfgFileMode os.FileMode = 0640
networkFileMode os.FileMode = 0644
initMask int = 0022
defaultISuladGraphPath = "/var/lib/isulad"
defaultRuntime = "lcr"
defaultStorageDriver = "overlay2"
)
var (
commonTool *IsuladTool
)
// IsuladTool contains the common functions used by transformer
type IsuladTool struct {
graphRoot string
runtime string
// storage
storageType StorageType
storageDriver BaseStorageDriver
}
func init() {
syscall.Umask(initMask)
}
// InitIsuladTool initializes the global iSuladCfgTool with the given parameters
func InitIsuladTool(graphRoot, runtime, storageDriver, imageSrvAddr string) error {
if graphRoot == "" {
graphRoot = defaultISuladGraphPath
}
if runtime == "" {
runtime = defaultRuntime
}
if storageDriver == "" {
storageDriver = defaultStorageDriver
}
commonTool = &IsuladTool{
graphRoot: graphRoot,
runtime: runtime,
storageType: StorageType(storageDriver),
}
if err := checkToolConfigValid(); err != nil {
logrus.Errorf("config of iSuladTool is invalid: %+v", commonTool)
return errors.Wrap(err, "config of iSuladTool is invalid")
}
if err := initBaseStorageDriver(imageSrvAddr); err != nil {
logrus.Errorf("init global base storage driver failed: %v", err)
return errors.Wrap(err, "init global base storage driver failed")
}
commonTool.storageDriver = gBaseStorageDriver
return nil
}
// GetIsuladCfgTool returns the global isuladtool
func GetIsuladCfgTool() *IsuladTool {
return commonTool
}
func checkToolConfigValid() error {
g := GetIsuladCfgTool()
// runtime
switch g.runtime {
case defaultRuntime:
default:
return fmt.Errorf("not support runtime: %s", g.runtime)
}
// storage driver
switch g.storageType {
case Overlay2, DeviceMapper:
default:
return fmt.Errorf("not support storage driver: %s", g.runtime)
}
return nil
}
// StorageType returns the storage type of isulad
func (ict *IsuladTool) StorageType() StorageType {
return ict.storageType
}
// BaseStorageDriver returns the global base storage driver tool
func (ict *IsuladTool) BaseStorageDriver() BaseStorageDriver {
return ict.storageDriver
}
// Runtime returns the runtime of isulad used
func (ict *IsuladTool) Runtime() string {
return ict.runtime
}
// GetRuntimePath returns the default runtime path of isulad
func (ict *IsuladTool) GetRuntimePath() string {
return filepath.Join(ict.graphRoot, "engines", ict.runtime)
}
// PrepareBundleDir creates runtime root dir of the container
func (ict *IsuladTool) PrepareBundleDir(id string) error {
path := filepath.Join(ict.GetRuntimePath(), id)
_, err := os.Stat(path)
if err == nil || os.IsExist(err) {
return fmt.Errorf("directory %s already exists, container has been or is being transformed", path)
}
return os.MkdirAll(path, rootDirMode)
}
// GetHostCfgPath returns path of hostconfig.json
func (ict *IsuladTool) GetHostCfgPath(id string) string {
return filepath.Join(ict.GetRuntimePath(), id, types.Hostconfig)
}
// GetConfigV2Path returns path of config.v2.json
func (ict *IsuladTool) GetConfigV2Path(id string) string {
return filepath.Join(ict.GetRuntimePath(), id, types.V2config)
}
// GetOciConfigPath returns path of config.json
func (ict *IsuladTool) GetOciConfigPath(id string) string {
return filepath.Join(ict.GetRuntimePath(), id, types.Ociconfig)
}
// GetNetworkFilePath returns the path specified file in host, hostname and resolv.conf
func (ict *IsuladTool) GetNetworkFilePath(id, file string) string {
return filepath.Join(ict.GetRuntimePath(), id, file)
}
// ReadData allows isuladTool to read data from a source
type ReadData func(src interface{}) ([]byte, error)
// FilePath allows isuladTool to obtain the path to the file to be written and saved
type FilePath func(string) string
// SaveConfig allows isuladTool to save data to file
func (ict *IsuladTool) SaveConfig(id string, src interface{}, read ReadData, getPath FilePath) error {
path := getPath(id)
_, err := os.Stat(path)
// getPath should not be exist here
if err == nil || os.IsExist(err) {
return errors.Errorf("%s already exist", path)
}
data, err := read(src)
if err != nil {
logrus.Errorf("save config read data internal error: %v", err)
return err
}
var mode os.FileMode
switch filepath.Base(path) {
case types.Hostname, types.Hosts, types.Resolv:
mode = networkFileMode
default:
mode = cfgFileMode
}
err = ioutil.WriteFile(path, data, mode)
if err != nil {
logrus.Errorf("write data(%s) to file %s failed: %v", string(data), path, err)
return err
}
return nil
}
// MarshalIndent formats the json bytes with indent
func (ict *IsuladTool) MarshalIndent(src interface{}) (bytes []byte, e error) {
return json.MarshalIndent(src, "", "\t")
}
// Cleanup remove runtime root dir of the container
func (ict *IsuladTool) Cleanup(id string) error {
path := filepath.Join(ict.GetRuntimePath(), id)
return os.RemoveAll(path)
}
// PrepareShm creates sharm shm mount point for container
func (ict *IsuladTool) PrepareShm(path string, size int64) error {
err := os.MkdirAll(path, mountsDirMode)
if err != nil {
return err
}
shmProperty := "mode=1777,size=" + strconv.FormatInt(size, 10)
err = unix.Mount("shm", path, "tmpfs", uintptr(unix.MS_NOEXEC|unix.MS_NOSUID|unix.MS_NODEV), shmProperty)
if err != nil {
return err
}
return nil
}
// LcrCreate calls lcr interface to init isulad container
func (ict *IsuladTool) LcrCreate(id string, spec []byte) error {
return lcrCreate(id, ict.GetRuntimePath(), spec)
}
/*
* Copyright (c) 2020 Huawei Technologies Co., Ltd.
* isula-transform is licensed under 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.
* Create: 2020-04-24
*/
package transform
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
. "github.com/smartystreets/goconvey/convey"
"golang.org/x/sys/unix"
"isula.org/isula-transform/types"
)
const itTestCtrID = "isulatransformittestctr"
var testIsuladTool = &IsuladTool{
graphRoot: "/var/lib/isulad",
runtime: "lcr",
storageType: Overlay2,
}
func TestInitIsuladTool(t *testing.T) {
Convey("TestInitIsuladTool", t, func() {
Convey("wrong container runtime", func() {
err := InitIsuladTool("", "kata", "", "")
So(err, ShouldNotBeNil)
So(err.Error(), ShouldContainSubstring, "not support runtime")
})
Convey("wrong storage driver", func() {
err := InitIsuladTool("", "", "aufs", "")
So(err, ShouldNotBeNil)
So(err.Error(), ShouldContainSubstring, "not support storage driver")
})
Convey("default init", func() {
So(InitIsuladTool("", "", "", ""), ShouldBeNil)
So(GetIsuladCfgTool().graphRoot, ShouldEqual, testIsuladTool.graphRoot)
So(GetIsuladCfgTool().runtime, ShouldEqual, testIsuladTool.runtime)
So(GetIsuladCfgTool().storageType, ShouldEqual, testIsuladTool.storageType)
So(GetIsuladCfgTool().storageDriver, ShouldNotBeNil)
})
})
}
func TestIsuladTool_GetterFunc(t *testing.T) {
Convey("TestIsuladTool_GetterFunc", t, func() {
Convey("StorageType", func() {
So(testIsuladTool.StorageType(), ShouldEqual, Overlay2)
})
Convey("BaseStorageDriver", func() {
So(testIsuladTool.BaseStorageDriver(), ShouldBeNil)
})
Convey("Runtime", func() {
So(testIsuladTool.Runtime(), ShouldEqual, defaultRuntime)
})
})
}
func TestIsuladTool_GetPathFunc(t *testing.T) {
Convey("TestIsuladTool_GetPathFunc", t, func() {
Convey("GetRuntimePath", func() {
want := "/var/lib/isulad/engines/lcr"
So(testIsuladTool.GetRuntimePath(), ShouldEqual, want)
})
Convey("GetHostCfgPath", func() {
want := filepath.Join("/var/lib/isulad/engines/lcr", itTestCtrID, types.Hostconfig)
So(testIsuladTool.GetHostCfgPath(itTestCtrID), ShouldEqual, want)
})
Convey("GetConfigV2Path", func() {
want := filepath.Join("/var/lib/isulad/engines/lcr", itTestCtrID, types.V2config)
So(testIsuladTool.GetConfigV2Path(itTestCtrID), ShouldEqual, want)
})
Convey("GetOciConfigPath", func() {
want := filepath.Join("/var/lib/isulad/engines/lcr", itTestCtrID, types.Ociconfig)
So(testIsuladTool.GetOciConfigPath(itTestCtrID), ShouldEqual, want)
})
Convey("GetNetworkFilePath", func() {
Convey("GetHostnamePath", func() {
want := filepath.Join("/var/lib/isulad/engines/lcr", itTestCtrID, types.Hostname)
So(testIsuladTool.GetNetworkFilePath(itTestCtrID, types.Hostname), ShouldEqual, want)
})
Convey("GetHostsPath", func() {
want := filepath.Join("/var/lib/isulad/engines/lcr", itTestCtrID, types.Hosts)
So(testIsuladTool.GetNetworkFilePath(itTestCtrID, types.Hosts), ShouldEqual, want)
})
Convey("GetResolvPath", func() {
want := filepath.Join("/var/lib/isulad/engines/lcr", itTestCtrID, types.Resolv)
So(testIsuladTool.GetNetworkFilePath(itTestCtrID, types.Resolv), ShouldEqual, want)
})
})
})
}
func TestIsuladTool_PrepareRootDir(t *testing.T) {
path := "/var/lib/isulad/engines/lcr/" + itTestCtrID
if err := os.RemoveAll(path); err != nil {
t.Skipf("before remove root dir: %v", err)
}
Convey("TestIsuladTool_PrepareRootDir", t, func() {
Convey("dir already exist", func() {
err := testIsuladTool.PrepareBundleDir(itTestCtrID)
defer os.RemoveAll(path)
So(err, ShouldBeNil)
err = testIsuladTool.PrepareBundleDir(itTestCtrID)
So(err, ShouldBeError)
So(err.Error(), ShouldContainSubstring, "already exists")
})
Convey("normal", func() {
err := testIsuladTool.PrepareBundleDir(itTestCtrID)
So(err, ShouldBeNil)
defer os.RemoveAll(path)
info, err := os.Stat(path)
So(err, ShouldBeNil)
So(info.IsDir(), ShouldBeTrue)
So(info.Mode(), ShouldEqual, rootDirMode|os.ModeDir)
})
})
}
func TestIsuladTool_SaveConfig(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "IsuladTool")
if err != nil {
t.Skipf("make temp dir: %v", err)
}
defer os.RemoveAll(tmpdir)
Convey("TestIsuladTool_SaveConfig", t, func() {
Convey("MarshalIndent", func() {
Convey("read normal", func() {
src := &struct {
Usage string
Indent struct{ Usage string }
}{
Usage: "test for IsuladTool.SaveConfig read normal",
Indent: struct{ Usage string }{Usage: "test for IsuladTool.MarshalIndent layers"},
}
getPath := func(string) string {
return filepath.Join(tmpdir, "test.json")
}
So(testIsuladTool.SaveConfig(itTestCtrID, src, testIsuladTool.MarshalIndent, getPath), ShouldBeNil)
got, _ := ioutil.ReadFile(getPath(itTestCtrID))
want := `{
"Usage": "test for IsuladTool.SaveConfig read normal",
"Indent": {
"Usage": "test for IsuladTool.MarshalIndent layers"
}
}`
So(string(got), ShouldEqual, want)
info, _ := os.Stat(getPath(itTestCtrID))
So(info.Mode(), ShouldEqual, cfgFileMode)
})
Convey("read abnormal", func() {
src := &struct{ C chan int }{C: make(chan int)}
getPath := func(string) string { return "" }
So(testIsuladTool.SaveConfig(itTestCtrID, src, testIsuladTool.MarshalIndent, getPath), ShouldBeError)
})
})
Convey("network file mode", func() {
read := func(src interface{}) ([]byte, error) {
return []byte("localhost"), nil
}
getPath := func(string) string {
return filepath.Join(tmpdir, "hosts")
}
So(testIsuladTool.SaveConfig(itTestCtrID, nil, read, getPath), ShouldBeNil)
got, _ := ioutil.ReadFile(getPath(itTestCtrID))
want := "localhost"
So(string(got), ShouldEqual, want)
info, _ := os.Stat(getPath(itTestCtrID))
So(info.Mode(), ShouldEqual, networkFileMode)
})
})
}
func TestIsuladTool_Cleanup(t *testing.T) {
path := "/var/lib/isulad/engines/lcr/" + itTestCtrID
if err := os.MkdirAll(path, rootDirMode); err != nil {
t.Skipf("make root dir: %v", err)
}
Convey("TestIsuladTool_Cleanup", t, func() {
err := testIsuladTool.Cleanup(itTestCtrID)
So(err, ShouldBeNil)
info, err := os.Stat(path)
So(info, ShouldBeNil)
So(err, ShouldNotBeNil)
So(err.Error(), ShouldContainSubstring, "no such file or directory")
})
}
func TestIsuladTool_PrepareShm(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "IsuladTool")
if err != nil {
t.Skipf("make temp dir: %v", err)
}
defer os.RemoveAll(tmpdir)
Convey("TestIsuladTool_PrepareShm", t, func() {
var shmPath = filepath.Join(tmpdir, "mounts/shm")
var shmSize int64 = 67108864
So(testIsuladTool.PrepareShm(shmPath, shmSize), ShouldBeEmpty)
defer func(path string) {
if err := unix.Unmount(path, unix.MNT_DETACH); err != nil {
t.Logf("umount path err: %v", err)
}
}(shmPath)
info, err := os.Stat(shmPath)
So(err, ShouldBeNil)
So(info.Mode(), ShouldEqual, os.ModeDir|os.ModeSticky|os.ModePerm)
})
}
/*
* Copyright (c) 2020 Huawei Technologies Co., Ltd.
* isula-transform is licensed under 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.
* Create: 2020-04-24
*/
package transform
/*
#cgo LDFLAGS: -L/usr/lib64 -llcr -llxc
#include <stdlib.h>
#include <lcr/lcrcontainer.h>
*/
import "C"
import (
"fmt"
"unsafe"
)
const isuladLogGatherFIFOName = "/isulad_log_gather_fifo"
// LcrLogInit initlizes the lcr log opt
func LcrLogInit(iSuladState, iSuladRuntime, logLevel string) {
name := C.CString("isulad")
file := C.CString("fifo:" + iSuladState + isuladLogGatherFIFOName)
priority := C.CString(logLevel)
prefix := C.CString(iSuladRuntime)
quiet := C.int(1)
defer func() {
cFreeChar(name, file, priority, prefix)
}()
C.lcr_log_init(name, file, priority, prefix, quiet, nil)
}
// lcrCreate call c func lcr_create_from_ocidata to create config, ocihooks.json and seccomp
func lcrCreate(id, lcrPath string, spec []byte) error {
name := C.CString(id)
lcrpath := C.CString(lcrPath)
ociConfigData := unsafe.Pointer(&spec[0])
defer func() {
cFreeChar(name, lcrpath)
}()
if !C.lcr_create_from_ocidata(name, lcrpath, ociConfigData) {
return fmt.Errorf("lcr create failed")
}
return nil
}
func cFreeChar(cs ...*C.char) {
for i := range cs {
C.free(unsafe.Pointer(cs[i]))
}
}
/*
* Copyright (c) 2020 Huawei Technologies Co., Ltd.
* isula-transform is licensed under 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.
* Create: 2020-04-24
*/
// Package register is used to register all kinds of transformer
package register
import (
// register docker transformer
_ "isula.org/isula-transform/transform/docker"
)
此差异已折叠。
此差异已折叠。
此差异已折叠。
/*
* Copyright (c) 2020 Huawei Technologies Co., Ltd.
* isula-transform is licensed under 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.
* Create: 2020-04-24
*/
package transform
import (
"flag"
"testing"
. "github.com/smartystreets/goconvey/convey"
"github.com/urfave/cli"
)
type mockTransformer struct{}
func newMockTransformer(*cli.Context) Transformer { return &mockTransformer{} }
func (mt *mockTransformer) Init() error { return nil }
func (mt *mockTransformer) Transform([]string, bool, chan Result) {}
func TestTransformers(t *testing.T) {
defer delete(transformers, "mock")
Convey("TestTransformers", t, func() {
Convey("TestRegister", func() {
Register("mock", newMockTransformer)
So(transformers, ShouldContainKey, "mock")
})
Convey("TestGetTransformer", func() {
flags := flag.NewFlagSet("", flag.ContinueOnError)
typeFlag := cli.StringFlag{Name: "container-type"}
Convey("Get support transformer", func() {
typeFlag.Value = "mock"
typeFlag.Apply(flags)
ctx := cli.NewContext(nil, flags, nil)
So(GetTransformer(ctx), ShouldNotBeNil)
})
Convey("Not support transformer", func() {
typeFlag.Value = "isulad"
typeFlag.Apply(flags)
ctx := cli.NewContext(nil, flags, nil)
So(GetTransformer(ctx), ShouldBeNil)
})
})
})
}
func TestEngineOpt(t *testing.T) {
Convey("TestEngineOpt", t, func() {
base := &BaseTransformer{}
Convey("TestEngineWithGraph", func() {
graph := "/test/lib/isulad"
opt := EngineWithGraph(graph)
opt(base)
So(base.GraphRoot, ShouldEqual, graph)
})
Convey("TestEngineWithState", func() {
state := "/test/run/isulad"
opt := EngineWithState(state)
opt(base)
So(base.StateRoot, ShouldEqual, state)
})
})
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册