github.com/alibaba/sealer@v0.8.6-0.20220430115802-37a2bdaa8173/pkg/image/default_image_file.go (about) 1 // Copyright © 2021 Alibaba Group Holding Ltd. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package image 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "io" 21 "io/ioutil" 22 "os" 23 "path/filepath" 24 25 "github.com/alibaba/sealer/pkg/image/types" 26 27 "sigs.k8s.io/yaml" 28 29 "github.com/alibaba/sealer/common" 30 "github.com/alibaba/sealer/logger" 31 "github.com/alibaba/sealer/pkg/image/store" 32 v1 "github.com/alibaba/sealer/types/api/v1" 33 "github.com/alibaba/sealer/utils" 34 "github.com/alibaba/sealer/utils/archive" 35 ) 36 37 type DefaultImageFileService struct { 38 layerStore store.LayerStore 39 imageStore store.ImageStore 40 } 41 42 func (d DefaultImageFileService) Load(imageSrc string) error { 43 var ( 44 srcFile *os.File 45 size int64 46 err error 47 repoFile = filepath.Join(common.DefaultLayerDir, common.DefaultMetadataName) 48 imageMetadataMap store.ImageMetadataMap 49 ) 50 51 srcFile, err = os.Open(filepath.Clean(imageSrc)) 52 if err != nil { 53 return fmt.Errorf("failed to open %s, err : %v", imageSrc, err) 54 } 55 defer func() { 56 if err := srcFile.Close(); err != nil { 57 logger.Error("failed to close file") 58 } 59 }() 60 61 srcFi, err := srcFile.Stat() 62 if err != nil { 63 return err 64 } 65 size = srcFi.Size() 66 67 if _, err = archive.Decompress(srcFile, common.DefaultLayerDir, archive.Options{Compress: false}); err != nil { 68 return err 69 } 70 71 repoBytes, err := ioutil.ReadFile(filepath.Clean(repoFile)) 72 if err != nil { 73 return err 74 } 75 if err := json.Unmarshal(repoBytes, &imageMetadataMap); err != nil { 76 return err 77 } 78 defer func() { 79 if err := os.Remove(repoFile); err != nil { 80 logger.Error("failed to close file") 81 } 82 }() 83 84 for name, repo := range imageMetadataMap { 85 for _, m := range repo.Manifests { 86 var image v1.Image 87 imageTempFile := filepath.Join(common.DefaultLayerDir, m.ID+".yaml") 88 if err = utils.UnmarshalYamlFile(imageTempFile, &image); err != nil { 89 return fmt.Errorf("failed to parsing %s, err: %v", imageTempFile, err) 90 } 91 for _, layer := range image.Spec.Layers { 92 if layer.ID == "" { 93 continue 94 } 95 roLayer, err := store.NewROLayer(layer.ID, size, nil) 96 if err != nil { 97 return err 98 } 99 100 err = d.layerStore.RegisterLayerIfNotPresent(roLayer) 101 if err != nil { 102 return fmt.Errorf("failed to register layer, err: %v", err) 103 } 104 } 105 err = d.imageStore.Save(image) 106 if err != nil { 107 return err 108 } 109 if err = os.Remove(imageTempFile); err != nil { 110 logger.Error("failed to cleanup local temp file %s:%v", imageTempFile, err) 111 } 112 } 113 logger.Info("load image %s successfully", name) 114 } 115 116 return nil 117 } 118 119 func (d DefaultImageFileService) Save(imageName, imageTar string, platforms []*v1.Platform) error { 120 var ( 121 pathsToCompress []string 122 ml []*types.ManifestDescriptor 123 repoData = make(store.ImageMetadataMap) 124 ) 125 126 manifestList, err := d.imageStore.GetImageManifestList(imageName) 127 if err != nil { 128 return err 129 } 130 131 if len(platforms) == 0 { 132 for _, m := range manifestList { 133 platforms = append(platforms, &m.Platform) 134 } 135 } 136 137 if err := utils.MkFileFullPathDir(imageTar); err != nil { 138 return fmt.Errorf("failed to create %s, err: %v", imageTar, err) 139 } 140 file, err := os.Create(filepath.Clean(imageTar)) 141 if err != nil { 142 return fmt.Errorf("failed to create %s, err: %v", imageTar, err) 143 } 144 defer func() { 145 if err := file.Close(); err != nil { 146 logger.Error("failed to close file") 147 } 148 }() 149 150 tempDir, err := utils.MkTmpdir() 151 if err != nil { 152 return fmt.Errorf("failed to create %s, err: %v", tempDir, err) 153 } 154 defer utils.CleanDir(tempDir) 155 156 repofile := filepath.Join(tempDir, common.DefaultMetadataName) 157 imageStore, err := store.NewDefaultImageStore() 158 if err != nil { 159 return err 160 } 161 162 // write image layer file and image yaml file. 163 for _, p := range platforms { 164 metadata, err := imageStore.GetImageMetadataItem(imageName, p) 165 if err != nil { 166 return err 167 } 168 ml = append(ml, metadata) 169 // add image layer 170 ima, err := imageStore.GetByName(imageName, p) 171 if err != nil { 172 return err 173 } 174 layerDirs, err := GetImageLayerDirs(ima) 175 if err != nil { 176 return err 177 } 178 pathsToCompress = append(pathsToCompress, layerDirs...) 179 // add image yaml 180 imgBytes, err := yaml.Marshal(ima) 181 if err != nil { 182 return fmt.Errorf("failed to marchal image, err: %s", err) 183 } 184 imagePath := filepath.Join(tempDir, ima.Spec.ID+".yaml") 185 if err = utils.AtomicWriteFile(imagePath, imgBytes, common.FileMode0644); err != nil { 186 return fmt.Errorf("failed to write temp file %s, err: %v ", imagePath, err) 187 } 188 pathsToCompress = append(pathsToCompress, imagePath) 189 } 190 191 repoData[imageName] = &types.ManifestList{Manifests: ml} 192 repoBytes, err := json.Marshal(repoData) 193 if err != nil { 194 return err 195 } 196 if err = utils.AtomicWriteFile(repofile, repoBytes, common.FileMode0644); err != nil { 197 return fmt.Errorf("failed to write temp file %s, err: %v ", repofile, err) 198 } 199 // add image repo data 200 pathsToCompress = append(pathsToCompress, repofile) 201 tarReader, err := archive.TarWithRootDir(pathsToCompress...) 202 if err != nil { 203 return fmt.Errorf("failed to get tar reader for %s, err: %s", imageName, err) 204 } 205 defer func() { 206 if err := tarReader.Close(); err != nil { 207 logger.Error("failed to close file") 208 } 209 }() 210 211 _, err = io.Copy(file, tarReader) 212 return err 213 } 214 215 func (d DefaultImageFileService) Merge(image *v1.Image) error { 216 panic("implement me") 217 }