// aciInfoToV1AlphaAPIImage takes an aciInfo object and construct the v1alpha.Image object. func aciInfoToV1AlphaAPIImage(store *imagestore.Store, aciInfo *imagestore.ACIInfo) (*v1alpha.Image, error) { manifest, err := store.GetImageManifestJSON(aciInfo.BlobKey) if err != nil { stderr.PrintE("failed to read the image manifest", err) return nil, err } var im schema.ImageManifest if err = json.Unmarshal(manifest, &im); err != nil { stderr.PrintE("failed to unmarshal image manifest", err) return nil, err } version, ok := im.Labels.Get("version") if !ok { version = "latest" } return &v1alpha.Image{ BaseFormat: &v1alpha.ImageFormat{ // Only support appc image now. If it's a docker image, then it // will be transformed to appc before storing in the disk store. Type: v1alpha.ImageType_IMAGE_TYPE_APPC, Version: schema.AppContainerVersion.String(), }, Id: aciInfo.BlobKey, Name: im.Name.String(), Version: version, ImportTimestamp: aciInfo.ImportTime.Unix(), Manifest: manifest, Size: aciInfo.Size + aciInfo.TreeStoreSize, Annotations: convertAnnotationsToKeyValue(im.Annotations), Labels: convertLabelsToKeyValue(im.Labels), }, nil }
// getTmpROC returns a removeOnClose instance wrapping a temporary // file provided by the passed store. The actual file name is based on // a hash of the passed path. func getTmpROC(s *imagestore.Store, path string) (*removeOnClose, error) { h := sha512.New() h.Write([]byte(path)) pathHash := s.HashToKey(h) tmp, err := s.TmpNamedFile(pathHash) if err != nil { return nil, errwrap.Wrap(errors.New("error setting up temporary file"), err) } // let's lock the file to avoid concurrent writes to the temporary file, it // will go away when removing the temp file _, err = lock.TryExclusiveLock(tmp.Name(), lock.RegFile) if err != nil { if err != lock.ErrLocked { return nil, errwrap.Wrap(errors.New("failed to lock temporary file"), err) } log.Printf("another rkt instance is downloading this file, waiting...") _, err = lock.ExclusiveLock(tmp.Name(), lock.RegFile) if err != nil { return nil, errwrap.Wrap(errors.New("failed to lock temporary file"), err) } } return &removeOnClose{File: tmp}, nil }
func rmImages(s *imagestore.Store, images []string) error { imageMap := make(map[string]string) for _, img := range images { key, err := getStoreKeyFromAppOrHash(s, img) if err != nil { stderr.Error(err) continue } aciinfo, err := s.GetACIInfoWithBlobKey(key) if err != nil { stderr.PrintE(fmt.Sprintf("error retrieving aci infos for image %q", key), err) continue } imageMap[key] = aciinfo.Name } done := 0 errors := 0 staleErrors := 0 for key, name := range imageMap { if err := s.RemoveACI(key); err != nil { if serr, ok := err.(*imagestore.StoreRemovalError); ok { staleErrors++ stderr.PrintE(fmt.Sprintf("some files cannot be removed for image %q (%q)", key, name), serr) } else { errors++ stderr.PrintE(fmt.Sprintf("error removing aci for image %q (%q)", key, name), err) continue } } stdout.Printf("successfully removed aci for image: %q", key) done++ } if done > 0 { stderr.Printf("%d image(s) successfully removed", done) } // If anything didn't complete, return exit status of 1 if (errors + staleErrors) > 0 { if staleErrors > 0 { stderr.Printf("%d image(s) removed but left some stale files", staleErrors) } if errors > 0 { stderr.Printf("%d image(s) cannot be removed", errors) } return fmt.Errorf("error(s) found while removing images") } return nil }
func remoteForURL(s *imagestore.Store, u *url.URL) (*imagestore.Remote, error) { urlStr := u.String() rem, err := s.GetRemote(urlStr) if err != nil { if err == imagestore.ErrRemoteNotFound { return nil, nil } return nil, errwrap.Wrap(fmt.Errorf("failed to fetch remote for URL %q", urlStr), err) } return rem, nil }
// getImageInfoFromDisk for a given image ID, returns the *v1alpha.Image object. func (s *v1AlphaAPIServer) getImageInfoFromDisk(store *imagestore.Store, imageID string) (*v1alpha.Image, error) { aciInfo, err := store.GetACIInfoWithBlobKey(imageID) if err != nil { stderr.PrintE(fmt.Sprintf("failed to get ACI info for image %q", imageID), err) return nil, err } image, err := aciInfoToV1AlphaAPIImage(store, aciInfo) if err != nil { stderr.PrintE(fmt.Sprintf("failed to convert ACI to v1alphaAPIImage for image %q", imageID), err) return nil, err } return image, nil }
func getStoreKeyFromAppOrHash(s *imagestore.Store, input string) (string, error) { var key string if _, err := types.NewHash(input); err == nil { key, err = s.ResolveKey(input) if err != nil { return "", errwrap.Wrap(errors.New("cannot resolve image ID"), err) } } else { key, err = getStoreKeyFromApp(s, input) if err != nil { return "", errwrap.Wrap(errors.New("cannot find image"), err) } } return key, nil }
func gcStore(s *imagestore.Store, gracePeriod time.Duration) error { var imagesToRemove []string aciinfos, err := s.GetAllACIInfos([]string{"lastused"}, true) if err != nil { return errwrap.Wrap(errors.New("failed to get aciinfos"), err) } for _, ai := range aciinfos { if time.Now().Sub(ai.LastUsed) <= gracePeriod { break } imagesToRemove = append(imagesToRemove, ai.BlobKey) } if err := rmImages(s, imagesToRemove); err != nil { return err } return nil }
func getStoreKeyFromApp(s *imagestore.Store, img string) (string, error) { app, err := discovery.NewAppFromString(img) if err != nil { return "", errwrap.Wrap(fmt.Errorf("cannot parse the image name %q", img), err) } labels, err := types.LabelsFromMap(app.Labels) if err != nil { return "", errwrap.Wrap(fmt.Errorf("invalid labels in the image %q", img), err) } key, err := s.GetACI(app.Name, labels) if err != nil { switch err.(type) { case imagestore.ACINotFoundError: return "", err default: return "", errwrap.Wrap(fmt.Errorf("cannot find image %q", img), err) } } return key, nil }
// TODO(sgotti) when the TreeStore will use an interface, change it to a // test implementation without relying on store/imagestore func testStoreWriteACI(dir string, s *imagestore.Store) (string, error) { imj := ` { "acKind": "ImageManifest", "acVersion": "0.8.8", "name": "example.com/test01" } ` entries := []*aci.ACIEntry{ // An empty dir { Header: &tar.Header{ Name: "rootfs/a", Typeflag: tar.TypeDir, }, }, { Contents: "hello", Header: &tar.Header{ Name: "hello.txt", Size: 5, }, }, { Header: &tar.Header{ Name: "rootfs/link.txt", Linkname: "rootfs/hello.txt", Typeflag: tar.TypeSymlink, }, }, // dangling symlink { Header: &tar.Header{ Name: "rootfs/link2.txt", Linkname: "rootfs/missingfile.txt", Typeflag: tar.TypeSymlink, }, }, { Header: &tar.Header{ Name: "rootfs/fifo", Typeflag: tar.TypeFifo, }, }, } aci, err := aci.NewACI(dir, imj, entries) if err != nil { return "", err } defer aci.Close() // Rewind the ACI if _, err := aci.Seek(0, 0); err != nil { return "", err } // Import the new ACI key, err := s.WriteACI(aci, imagestore.ACIFetchInfo{Latest: false}) if err != nil { return "", err } return key, nil }
func rmImages(s *imagestore.Store, images []string) error { done := 0 errors := 0 staleErrors := 0 imageMap := make(map[string]string) imageCounter := make(map[string]int) for _, pkey := range images { errors++ h, err := types.NewHash(pkey) if err != nil { var found bool keys, found, err := s.ResolveName(pkey) if len(keys) > 0 { errors += len(keys) - 1 } if err != nil { stderr.Error(err) continue } if !found { stderr.Printf("image name %q not found", pkey) continue } for _, key := range keys { imageMap[key] = pkey imageCounter[key]++ } } else { key, err := s.ResolveKey(h.String()) if err != nil { stderr.PrintE(fmt.Sprintf("image ID %q not valid", pkey), err) continue } if key == "" { stderr.Printf("image ID %q doesn't exist", pkey) continue } aciinfo, err := s.GetACIInfoWithBlobKey(key) if err != nil { stderr.PrintE(fmt.Sprintf("error retrieving aci infos for image %q", key), err) continue } imageMap[key] = aciinfo.Name imageCounter[key]++ } } // Adjust the error count by subtracting duplicate IDs from it, // therefore allowing only one error per ID. for _, c := range imageCounter { if c > 1 { errors -= c - 1 } } for key, name := range imageMap { if err := s.RemoveACI(key); err != nil { if serr, ok := err.(*imagestore.StoreRemovalError); ok { staleErrors++ stderr.PrintE(fmt.Sprintf("some files cannot be removed for image %q (%q)", key, name), serr) } else { stderr.PrintE(fmt.Sprintf("error removing aci for image %q (%q)", key, name), err) continue } } stdout.Printf("successfully removed aci for image: %q", key) errors-- done++ } if done > 0 { stderr.Printf("%d image(s) successfully removed", done) } // If anything didn't complete, return exit status of 1 if (errors + staleErrors) > 0 { if staleErrors > 0 { stderr.Printf("%d image(s) removed but left some stale files", staleErrors) } if errors > 0 { stderr.Printf("%d image(s) cannot be removed", errors) } return fmt.Errorf("error(s) found while removing images") } return nil }