github.com/coreos/rocket@v1.30.1-0.20200224141603-171c416fac02/rkt/image_render.go (about) 1 // Copyright 2015 The rkt Authors 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 main 16 17 import ( 18 "encoding/json" 19 "io/ioutil" 20 "os" 21 "path/filepath" 22 23 "github.com/rkt/rkt/pkg/fileutil" 24 "github.com/rkt/rkt/pkg/user" 25 "github.com/rkt/rkt/store/imagestore" 26 "github.com/rkt/rkt/store/treestore" 27 28 "github.com/spf13/cobra" 29 ) 30 31 var ( 32 cmdImageRender = &cobra.Command{ 33 Use: "render IMAGE OUTPUT_DIR", 34 Short: "Render a stored image to a directory with all its dependencies", 35 Long: `IMAGE should be a string referencing an image: either a ID or an image name. 36 37 This differs from extract in that the rendered image is in the state the app 38 would see when running in rkt, dependencies and all. 39 40 Note that in order to make cleaning up easy (just rm -rf), this does not use 41 overlayfs or any other mechanism.`, 42 Run: runWrapper(runImageRender), 43 } 44 flagRenderRootfsOnly bool 45 flagRenderOverwrite bool 46 ) 47 48 func init() { 49 cmdImage.AddCommand(cmdImageRender) 50 cmdImageRender.Flags().BoolVar(&flagRenderRootfsOnly, "rootfs-only", false, "render rootfs only") 51 cmdImageRender.Flags().BoolVar(&flagRenderOverwrite, "overwrite", false, "overwrite output directory") 52 } 53 54 func runImageRender(cmd *cobra.Command, args []string) (exit int) { 55 if len(args) != 2 { 56 cmd.Usage() 57 return 254 58 } 59 outputDir := args[1] 60 61 s, err := imagestore.NewStore(storeDir()) 62 if err != nil { 63 stderr.PrintE("cannot open store", err) 64 return 254 65 } 66 67 ts, err := treestore.NewStore(treeStoreDir(), s) 68 if err != nil { 69 stderr.PrintE("cannot open store", err) 70 return 71 } 72 73 key, err := getStoreKeyFromAppOrHash(s, args[0]) 74 if err != nil { 75 stderr.Error(err) 76 return 254 77 } 78 79 id, _, err := ts.Render(key, false) 80 if err != nil { 81 stderr.PrintE("error rendering ACI", err) 82 return 254 83 } 84 if _, err := ts.Check(id); err != nil { 85 stderr.Print("warning: tree cache is in a bad state. Rebuilding...") 86 var err error 87 if id, _, err = ts.Render(key, true); err != nil { 88 stderr.PrintE("error rendering ACI", err) 89 return 254 90 } 91 } 92 93 if _, err := os.Stat(outputDir); err == nil { 94 if !flagRenderOverwrite { 95 stderr.Print("output directory exists (try --overwrite)") 96 return 254 97 } 98 99 // don't allow the user to delete the root filesystem by mistake 100 if outputDir == "/" { 101 stderr.Print("this would delete your root filesystem. Refusing.") 102 return 254 103 } 104 105 if err := os.RemoveAll(outputDir); err != nil { 106 stderr.PrintE("error removing existing output dir", err) 107 return 254 108 } 109 } 110 rootfsOutDir := outputDir 111 if !flagRenderRootfsOnly { 112 if err := os.MkdirAll(outputDir, 0755); err != nil { 113 stderr.PrintE("error creating output directory", err) 114 return 254 115 } 116 rootfsOutDir = filepath.Join(rootfsOutDir, "rootfs") 117 118 manifest, err := s.GetImageManifest(key) 119 if err != nil { 120 stderr.PrintE("error getting manifest", err) 121 return 254 122 } 123 124 mb, err := json.Marshal(manifest) 125 if err != nil { 126 stderr.PrintE("error marshalling image manifest", err) 127 return 254 128 } 129 130 if err := ioutil.WriteFile(filepath.Join(outputDir, "manifest"), mb, 0700); err != nil { 131 stderr.PrintE("error writing image manifest", err) 132 return 254 133 } 134 } 135 136 cachedTreePath := ts.GetRootFS(id) 137 if err := fileutil.CopyTree(cachedTreePath, rootfsOutDir, user.NewBlankUidRange()); err != nil { 138 stderr.PrintE("error copying ACI rootfs", err) 139 return 254 140 } 141 142 return 0 143 }