func GenerateAncestry(s storage.Storage, imageID, parentID string) (err error) { logger.Debug("[GenerateAncestry] imageID=" + imageID + " parentID=" + parentID) path := storage.ImageAncestryPath(imageID) if parentID == "" { return s.Put(path, []byte(`["`+imageID+`"]`)) } var content []byte if content, err = s.Get(storage.ImageAncestryPath(parentID)); err != nil { return err } var ancestry []string if err := json.Unmarshal(content, &ancestry); err != nil { return err } ancestry = append([]string{imageID}, ancestry...) if content, err = json.Marshal(&ancestry); err != nil { return err } return s.Put(path, content) }
func GenDiff(s storage.Storage, imageID string) { // Comment from docker-registry 0.6.5 // get json describing file differences in layer // Calculate the diff information for the files contained within // the layer. Return a dictionary of lists grouped by whether they // were deleted, changed or created in this layer. // To determine what happened to a file in a layer we walk backwards // through the ancestry until we see the file in an older layer. Based // on whether the file was previously deleted or not we know whether // the file was created or modified. If we do not find the file in an // ancestor we know the file was just created. // - File marked as deleted by union fs tar: DELETED // - Ancestor contains non-deleted file: CHANGED // - Ancestor contains deleted marked file: CREATED // - No ancestor contains file: CREATED diffJson, err := GetImageDiffCache(s, imageID) if err == nil && diffJson != nil { // cache hit, just return logger.Debug("[GenDiff][" + imageID + "] already exists") return } anPath := storage.ImageAncestryPath(imageID) anContent, err := s.Get(anPath) if err != nil { // error fetching ancestry, just return logger.Error("[GenDiff][" + imageID + "] error fetching ancestry: " + err.Error()) return } var ancestry []string if err := json.Unmarshal(anContent, &ancestry); err != nil { // json unmarshal fail, just return logger.Error("[GenDiff][" + imageID + "] error unmarshalling ancestry json: " + err.Error()) return } // get map of file infos infoMap, err := fileInfoMap(s, imageID) if err != nil { // error getting file info, just return logger.Error("[GenDiff][" + imageID + "] error getting files info: " + err.Error()) return } deleted := map[string][]interface{}{} changed := map[string][]interface{}{} created := map[string][]interface{}{} for _, anID := range ancestry { anInfoMap, err := fileInfoMap(s, anID) if err != nil { // error getting file info, just return logger.Error("[GenDiff][" + imageID + "] error getting ancestor " + anID + " files info: " + err.Error()) return } for fname, info := range infoMap { isDeleted, isBool := (info[1]).(bool) // if the file info is in a bad format (isDeleted is not a bool), we should just assume it is deleted. // technically isBool should never be false. if !isBool || isDeleted { if !isBool { logger.Error("[GenDiff][" + imageID + "] file info is in a bad format") } deleted[fname] = info delete(infoMap, fname) continue } anInfo := anInfoMap[fname] if err != nil || anInfo == nil { // doesn't exist, must be created. do nothing. continue } isDeleted, isBool = anInfo[1].(bool) if !isBool || isDeleted { if !isBool { logger.Error("[GenDiff][" + imageID + "] file info is in a bad format") } // deleted in ancestor, must be created now. created[fname] = info } else { // not deleted in ancestor, must have just changed now. changed[fname] = info } delete(infoMap, fname) } } // dump all created stuff from infoMap for fname, info := range infoMap { created[fname] = info } diff := map[string]map[string][]interface{}{ "deleted": deleted, "changed": changed, "created": created, } if diffJson, err = json.Marshal(&diff); err != nil { // json marshal fail. just return logger.Error("[GenDiff][" + imageID + "] error marshalling new diff json: " + err.Error()) return } if err := SetImageDiffCache(s, imageID, diffJson); err != nil { // json marshal fail. just return logger.Error("[GenDiff][" + imageID + "] error setting new diff cache: " + err.Error()) return } }