github.com/opencontainers/umoci@v0.4.8-0.20240508124516-656e4836fb0d/cmd/umoci/raw-runtime-config.go (about) 1 /* 2 * umoci: Umoci Modifies Open Containers' Images 3 * Copyright (C) 2016-2020 SUSE LLC 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package main 19 20 import ( 21 "context" 22 "fmt" 23 "os" 24 25 "github.com/apex/log" 26 ispec "github.com/opencontainers/image-spec/specs-go/v1" 27 "github.com/opencontainers/umoci" 28 "github.com/opencontainers/umoci/oci/cas/dir" 29 "github.com/opencontainers/umoci/oci/casext" 30 "github.com/opencontainers/umoci/oci/layer" 31 "github.com/pkg/errors" 32 "github.com/urfave/cli" 33 ) 34 35 var rawConfigCommand = uxRemap(cli.Command{ 36 Name: "runtime-config", 37 Aliases: []string{"config"}, 38 Usage: "generates an OCI runtime configuration for an image", 39 ArgsUsage: `--image <image-path>[:<tag>] [--rootfs <rootfs>] <config.json> 40 41 Where "<image-path>" is the path to the OCI image, "<tag>" is the name of the 42 tagged image to unpack (if not specified, defaults to "latest"), "<rootfs>" is 43 a rootfs to use as a supplementary "source of truth" for certain generation 44 operations and "<config.json>" is the destination to write the runtime 45 configuration to. 46 47 Note that the results of this may not agree with umoci-unpack(1) because the 48 --rootfs flag affects how certain properties are interpreted.`, 49 50 // unpack reads manifest information. 51 Category: "image", 52 53 Flags: []cli.Flag{ 54 cli.StringFlag{ 55 Name: "rootfs", 56 Usage: "path to secondary source of truth (root filesystem)", 57 }, 58 }, 59 60 Action: rawConfig, 61 62 Before: func(ctx *cli.Context) error { 63 if ctx.NArg() != 1 { 64 return errors.Errorf("invalid number of positional arguments: expected <config.json>") 65 } 66 if ctx.Args().First() == "" { 67 return errors.Errorf("config.json path cannot be empty") 68 } 69 ctx.App.Metadata["config"] = ctx.Args().First() 70 return nil 71 }, 72 }) 73 74 func rawConfig(ctx *cli.Context) error { 75 imagePath := ctx.App.Metadata["--image-path"].(string) 76 fromName := ctx.App.Metadata["--image-tag"].(string) 77 configPath := ctx.App.Metadata["config"].(string) 78 79 var meta umoci.Meta 80 meta.Version = umoci.MetaVersion 81 82 // Parse and set up the mapping options. 83 err := umoci.ParseIdmapOptions(&meta, ctx) 84 if err != nil { 85 return err 86 } 87 88 // Get a reference to the CAS. 89 engine, err := dir.Open(imagePath) 90 if err != nil { 91 return errors.Wrap(err, "open CAS") 92 } 93 engineExt := casext.NewEngine(engine) 94 defer engine.Close() 95 96 fromDescriptorPaths, err := engineExt.ResolveReference(context.Background(), fromName) 97 if err != nil { 98 return errors.Wrap(err, "get descriptor") 99 } 100 if len(fromDescriptorPaths) == 0 { 101 return errors.Errorf("tag not found: %s", fromName) 102 } 103 if len(fromDescriptorPaths) != 1 { 104 // TODO: Handle this more nicely. 105 return errors.Errorf("tag is ambiguous: %s", fromName) 106 } 107 meta.From = fromDescriptorPaths[0] 108 109 manifestBlob, err := engineExt.FromDescriptor(context.Background(), meta.From.Descriptor()) 110 if err != nil { 111 return errors.Wrap(err, "get manifest") 112 } 113 defer manifestBlob.Close() 114 115 if manifestBlob.Descriptor.MediaType != ispec.MediaTypeImageManifest { 116 return errors.Wrap(fmt.Errorf("descriptor does not point to ispec.MediaTypeImageManifest: not implemented: %s", manifestBlob.Descriptor.MediaType), "invalid --image tag") 117 } 118 119 // Get the manifest. 120 manifest, ok := manifestBlob.Data.(ispec.Manifest) 121 if !ok { 122 // Should _never_ be reached. 123 return errors.Errorf("[internal error] unknown manifest blob type: %s", manifestBlob.Descriptor.MediaType) 124 } 125 126 // Generate the configuration. 127 configFile, err := os.Create(configPath) 128 if err != nil { 129 return errors.Wrap(err, "opening config path") 130 } 131 defer configFile.Close() 132 133 // Write out the generated config. 134 log.Info("generating config.json") 135 if err := layer.UnpackRuntimeJSON(context.Background(), engineExt, configFile, ctx.String("rootfs"), manifest, &meta.MapOptions); err != nil { 136 return errors.Wrap(err, "generate config") 137 } 138 return nil 139 }