今天主要来实现一下
go-docker ps
的功能,也就是查看当前有哪些容器,简单说下思路,当我们启动一个容器时就为该容器创建一个文件夹用来保存该容器的一些信息,如果我们给容器指定了名字,那么该文件夹名字就是我们指定的名字,如果未指定,就用我们自动生成的容器ID作为文件夹名,同时在该文件夹中创建config.json
用来保存容器信息
文件夹结构
go-docker
└── 容器名/容器ID
├── config.json
└── container.log
- config.json 记录容器基础信息
- container.log 记录容器内容日志
记录容器信息
创建容器时,同时给该容器创建一个文件夹,文件夹内并创建config.json 来保存容器信息,这里先看下我们要保存容器哪些信息
type ContainerInfo struct {
Pid string `json:"pid"` // 容器的init进程再宿主机上的PID
Id string `json:"id"` // 容器ID
Command string `json:"command"` // 容器内init进程的运行命令
Name string `json:"name"`
CreateTime string `json:"createTime"`
Status string `json:"status"`
Volume string `json:"volume"` //容器的数据卷
PortMapping []string `json:"portmapping"` //端口映射
}
看下代码实现,比较简单,一共就3步
- 创建以容器名或ID命名的文件夹
- 在该文件下创建config.json
- 将容器信息保存到config.json中
// 记录容器信息
func RecordContainerInfo(containerPID int, cmdArray []string, containerName, containerID string) error {
info := &ContainerInfo{
Pid: strconv.Itoa(containerPID),
Id: containerID,
Command: strings.Join(cmdArray, ""),
Name: containerName,
CreateTime: time.Now().Format("2006-01-02 15:04:05"),
Status: common.Running,
}
dir := path.Join(common.DefaultContainerInfoPath, containerName)
_, err := os.Stat(dir)
if err != nil && os.IsNotExist(err) {
err := os.MkdirAll(dir, os.ModePerm)
if err != nil {
logrus.Errorf("mkdir container dir: %s, err: %v", dir, err)
return err
}
}
fileName := fmt.Sprintf("%s/%s", dir, common.ContainerInfoFileName)
file, err := os.Create(fileName)
if err != nil {
logrus.Errorf("create config.json, fileName: %s, err: %v", fileName, err)
return err
}
bs, _ := json.Marshal(info)
_, err = file.WriteString(string(bs))
if err != nil {
logrus.Errorf("write config.json, fileName: %s, err: %v", fileName, err)
return err
}
return nil
}
遍历容器
简单说下实现逻辑
- 遍历
go-docker
文件夹 - 读取每个容器内的config.json文件
- 格式化打印
func ListContainerInfo() {
files, err := ioutil.ReadDir(common.DefaultContainerInfoPath)
if err != nil {
logrus.Errorf("read info dir, err: %v", err)
}
var infos []*ContainerInfo
for _, file := range files {
info, err := getContainerInfo(file.Name())
if err != nil {
logrus.Errorf("get container info, name: %s, err: %v", file.Name(), err)
continue
}
infos = append(infos, info)
}
// 打印
w := tabwriter.NewWriter(os.Stdout, 12, 1, 2, ' ', 0)
_, _ = fmt.Fprint(w, "ID\tNAME\tPID\tSTATUS\tCOMMAND\tCREATED\n")
for _, info := range infos {
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t\n", info.Id, info.Name, info.Pid, info.Status, info.Command, info.CreateTime)
}
// 刷新标准输出流缓存区,将容器列表打印出来
if err := w.Flush(); err != nil {
logrus.Errorf("flush info, err: %v", err)
}
}
// 获取容器内基本信息
func getContainerInfo(containerName string) (*ContainerInfo, error) {
filePath := path.Join(common.DefaultContainerInfoPath, containerName, common.ContainerInfoFileName)
bs, err := ioutil.ReadFile(filePath)
if err != nil {
logrus.Errorf("read file, path: %s, err: %v", filePath, err)
return nil, err
}
info := &ContainerInfo{}
err = json.Unmarshal(bs, info)
return info, err
}