七天用Go寫個docker(第六天)

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

  1. 創建以容器名或ID命名的文件夾
  2. 在該文件下創建config.json
  3. 將容器信息保存到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
}

遍歷容器

簡單說下實現邏輯

  1. 遍歷 go-docker文件夾
  2. 讀取每個容器內的config.json文件
  3. 格式化打印
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
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章