github.com/containerd/nerdctl/v2@v2.0.0-beta.5.0.20240520001846-b5758f54fa28/pkg/snapshotterutil/sociutil.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package snapshotterutil 18 19 import ( 20 "bufio" 21 "os" 22 "os/exec" 23 "strconv" 24 "strings" 25 26 "github.com/containerd/log" 27 "github.com/containerd/nerdctl/v2/pkg/api/types" 28 ) 29 30 // CreateSoci creates a SOCI index(`rawRef`) 31 func CreateSoci(rawRef string, gOpts types.GlobalCommandOptions, allPlatform bool, platforms []string, sOpts types.SociOptions) error { 32 sociExecutable, err := exec.LookPath("soci") 33 if err != nil { 34 log.L.WithError(err).Error("soci executable not found in path $PATH") 35 log.L.Info("you might consider installing soci from: https://github.com/awslabs/soci-snapshotter/blob/main/docs/install.md") 36 return err 37 } 38 39 sociCmd := exec.Command(sociExecutable) 40 sociCmd.Env = os.Environ() 41 42 // #region for global flags. 43 if gOpts.Address != "" { 44 sociCmd.Args = append(sociCmd.Args, "--address", gOpts.Address) 45 } 46 if gOpts.Namespace != "" { 47 sociCmd.Args = append(sociCmd.Args, "--namespace", gOpts.Namespace) 48 } 49 // #endregion 50 51 // Global flags have to be put before subcommand before soci upgrades to urfave v3. 52 // https://github.com/urfave/cli/issues/1113 53 sociCmd.Args = append(sociCmd.Args, "create") 54 55 if allPlatform { 56 sociCmd.Args = append(sociCmd.Args, "--all-platforms") 57 } 58 if len(platforms) > 0 { 59 // multiple values need to be passed as separate, repeating flags in soci as it uses urfave 60 // https://github.com/urfave/cli/blob/main/docs/v2/examples/flags.md#multiple-values-per-single-flag 61 for _, p := range platforms { 62 sociCmd.Args = append(sociCmd.Args, "--platform", p) 63 } 64 } 65 66 if sOpts.SpanSize != -1 { 67 sociCmd.Args = append(sociCmd.Args, "--span-size", strconv.FormatInt(sOpts.SpanSize, 10)) 68 } 69 if sOpts.MinLayerSize != -1 { 70 sociCmd.Args = append(sociCmd.Args, "--min-layer-size", strconv.FormatInt(sOpts.MinLayerSize, 10)) 71 } 72 // --timeout, --debug, --content-store 73 sociCmd.Args = append(sociCmd.Args, rawRef) 74 75 log.L.Debugf("running %s %v", sociExecutable, sociCmd.Args) 76 77 err = processSociIO(sociCmd) 78 if err != nil { 79 return err 80 } 81 82 return sociCmd.Wait() 83 } 84 85 // PushSoci pushes a SOCI index(`rawRef`) 86 // `hostsDirs` are used to resolve image `rawRef` 87 func PushSoci(rawRef string, gOpts types.GlobalCommandOptions, allPlatform bool, platforms []string) error { 88 log.L.Debugf("pushing SOCI index: %s", rawRef) 89 90 sociExecutable, err := exec.LookPath("soci") 91 if err != nil { 92 log.L.WithError(err).Error("soci executable not found in path $PATH") 93 log.L.Info("you might consider installing soci from: https://github.com/awslabs/soci-snapshotter/blob/main/docs/install.md") 94 return err 95 } 96 97 sociCmd := exec.Command(sociExecutable) 98 sociCmd.Env = os.Environ() 99 100 // #region for global flags. 101 if gOpts.Address != "" { 102 sociCmd.Args = append(sociCmd.Args, "--address", gOpts.Address) 103 } 104 if gOpts.Namespace != "" { 105 sociCmd.Args = append(sociCmd.Args, "--namespace", gOpts.Namespace) 106 } 107 // #endregion 108 109 // Global flags have to be put before subcommand before soci upgrades to urfave v3. 110 // https://github.com/urfave/cli/issues/1113 111 sociCmd.Args = append(sociCmd.Args, "push") 112 113 if allPlatform { 114 sociCmd.Args = append(sociCmd.Args, "--all-platforms") 115 } 116 if len(platforms) > 0 { 117 // multiple values need to be passed as separate, repeating flags in soci as it uses urfave 118 // https://github.com/urfave/cli/blob/main/docs/v2/examples/flags.md#multiple-values-per-single-flag 119 for _, p := range platforms { 120 sociCmd.Args = append(sociCmd.Args, "--platform", p) 121 } 122 } 123 if gOpts.InsecureRegistry { 124 sociCmd.Args = append(sociCmd.Args, "--skip-verify") 125 sociCmd.Args = append(sociCmd.Args, "--plain-http") 126 } 127 if len(gOpts.HostsDir) > 0 { 128 sociCmd.Args = append(sociCmd.Args, "--hosts-dir") 129 sociCmd.Args = append(sociCmd.Args, strings.Join(gOpts.HostsDir, ",")) 130 } 131 sociCmd.Args = append(sociCmd.Args, rawRef) 132 133 log.L.Debugf("running %s %v", sociExecutable, sociCmd.Args) 134 135 err = processSociIO(sociCmd) 136 if err != nil { 137 return err 138 } 139 return sociCmd.Wait() 140 } 141 142 func processSociIO(sociCmd *exec.Cmd) error { 143 stdout, err := sociCmd.StdoutPipe() 144 if err != nil { 145 log.L.Warn("soci: " + err.Error()) 146 } 147 stderr, err := sociCmd.StderrPipe() 148 if err != nil { 149 log.L.Warn("soci: " + err.Error()) 150 } 151 if err := sociCmd.Start(); err != nil { 152 // only return err if it's critical (soci command failed to start.) 153 return err 154 } 155 156 scanner := bufio.NewScanner(stdout) 157 for scanner.Scan() { 158 log.L.Info("soci: " + scanner.Text()) 159 } 160 if err := scanner.Err(); err != nil { 161 log.L.Warn("soci: " + err.Error()) 162 } 163 164 errScanner := bufio.NewScanner(stderr) 165 for errScanner.Scan() { 166 log.L.Info("soci: " + errScanner.Text()) 167 } 168 if err := errScanner.Err(); err != nil { 169 log.L.Warn("soci: " + err.Error()) 170 } 171 172 return nil 173 }