今天主要來實現一下
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
}