github.com/dctrud/umoci@v0.4.3-0.20191016193643-05a1d37de015/cmd/umoci/utils_ux.go (about) 1 /* 2 * umoci: Umoci Modifies Open Containers' Images 3 * Copyright (C) 2016, 2017, 2018 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 "fmt" 22 "strings" 23 24 "github.com/openSUSE/umoci/oci/casext" 25 "github.com/pkg/errors" 26 "github.com/urfave/cli" 27 ) 28 29 func flattenCommands(cmds []cli.Command) []*cli.Command { 30 var flatten []*cli.Command 31 for idx, cmd := range cmds { 32 flatten = append(flatten, &cmds[idx]) 33 flatten = append(flatten, flattenCommands(cmd.Subcommands)...) 34 } 35 return flatten 36 } 37 38 // uxHistory adds the full set of --history.* flags to the given cli.Command as 39 // well as adding relevant validation logic to the .Before of the command. The 40 // values will be stored in ctx.Metadata with the keys "--history.author", 41 // "--history.created", "--history.created_by", "--history.comment", with 42 // string values. If they are not set the value will be nil. 43 func uxHistory(cmd cli.Command) cli.Command { 44 cmd.Flags = append(cmd.Flags, []cli.Flag{ 45 cli.StringFlag{ 46 Name: "history.author", 47 Usage: "author value for the history entry", 48 }, 49 cli.StringFlag{ 50 Name: "history.comment", 51 Usage: "comment for the history entry", 52 }, 53 cli.StringFlag{ 54 Name: "history.created", 55 Usage: "created value for the history entry", 56 }, 57 cli.StringFlag{ 58 Name: "history.created_by", 59 Usage: "created_by value for the history entry", 60 }, 61 }...) 62 63 oldBefore := cmd.Before 64 cmd.Before = func(ctx *cli.Context) error { 65 // Verify --history.author. 66 if ctx.IsSet("history.author") { 67 ctx.App.Metadata["--history.author"] = ctx.String("history.author") 68 } 69 // Verify --history.comment. 70 if ctx.IsSet("history.comment") { 71 ctx.App.Metadata["--history.comment"] = ctx.String("history.comment") 72 } 73 // Verify --history.created. 74 if ctx.IsSet("history.created") { 75 ctx.App.Metadata["--history.created"] = ctx.String("history.created") 76 } 77 // Verify --history.created_by. 78 if ctx.IsSet("history.created_by") { 79 ctx.App.Metadata["--history.created_by"] = ctx.String("history.created_by") 80 } 81 82 // Include any old befores set. 83 if oldBefore != nil { 84 return oldBefore(ctx) 85 } 86 return nil 87 } 88 89 return cmd 90 } 91 92 // uxTag adds a --tag flag to the given cli.Command as well as adding relevant 93 // validation logic to the .Before of the command. The value will be stored in 94 // ctx.Metadata["--tag"] as a string (or nil if --tag was not specified). 95 func uxTag(cmd cli.Command) cli.Command { 96 cmd.Flags = append(cmd.Flags, cli.StringFlag{ 97 Name: "tag", 98 Usage: "tag name", 99 }) 100 101 oldBefore := cmd.Before 102 cmd.Before = func(ctx *cli.Context) error { 103 // Verify tag value. 104 if ctx.IsSet("tag") { 105 tag := ctx.String("tag") 106 if !casext.IsValidReferenceName(tag) { 107 return errors.Wrap(fmt.Errorf("tag contains invalid characters: '%s'", tag), "invalid --tag") 108 } 109 if tag == "" { 110 return errors.Wrap(fmt.Errorf("tag is empty"), "invalid --tag") 111 } 112 ctx.App.Metadata["--tag"] = tag 113 } 114 115 // Include any old befores set. 116 if oldBefore != nil { 117 return oldBefore(ctx) 118 } 119 return nil 120 } 121 122 return cmd 123 } 124 125 // uxImage adds an --image flag to the given cli.Command as well as adding 126 // relevant validation logic to the .Before of the command. The values (image, 127 // tag) will be stored in ctx.Metadata["--image-path"] and 128 // ctx.Metadata["--image-tag"] as strings (both will be nil if --image is not 129 // specified). 130 func uxImage(cmd cli.Command) cli.Command { 131 cmd.Flags = append(cmd.Flags, cli.StringFlag{ 132 Name: "image", 133 Usage: "OCI image URI of the form 'path[:tag]'", 134 }) 135 136 oldBefore := cmd.Before 137 cmd.Before = func(ctx *cli.Context) error { 138 // Verify and parse --image. 139 if ctx.IsSet("image") { 140 image := ctx.String("image") 141 142 var dir, tag string 143 sep := strings.LastIndex(image, ":") 144 if sep == -1 { 145 dir = image 146 tag = "latest" 147 } else { 148 dir = image[:sep] 149 tag = image[sep+1:] 150 } 151 152 // Verify directory value. 153 if strings.Contains(dir, ":") { 154 return errors.Wrap(fmt.Errorf("path contains ':' character: '%s'", dir), "invalid --image") 155 } 156 if dir == "" { 157 return errors.Wrap(fmt.Errorf("path is empty"), "invalid --image") 158 } 159 160 // Verify tag value. 161 if !casext.IsValidReferenceName(tag) { 162 return errors.Wrap(fmt.Errorf("tag contains invalid characters: '%s'", tag), "invalid --image") 163 } 164 if tag == "" { 165 return errors.Wrap(fmt.Errorf("tag is empty"), "invalid --image") 166 } 167 168 ctx.App.Metadata["--image-path"] = dir 169 ctx.App.Metadata["--image-tag"] = tag 170 } 171 172 if oldBefore != nil { 173 return oldBefore(ctx) 174 } 175 return nil 176 } 177 178 return cmd 179 } 180 181 // uxLayout adds an --layout flag to the given cli.Command as well as adding 182 // relevant validation logic to the .Before of the command. The value is stored 183 // in ctx.App.Metadata["--image-path"] as a string (or nil --layout was not set). 184 func uxLayout(cmd cli.Command) cli.Command { 185 cmd.Flags = append(cmd.Flags, cli.StringFlag{ 186 Name: "layout", 187 Usage: "path to an OCI image layout", 188 }) 189 190 oldBefore := cmd.Before 191 cmd.Before = func(ctx *cli.Context) error { 192 // Verify and parse --layout. 193 if ctx.IsSet("layout") { 194 layout := ctx.String("layout") 195 196 // Verify directory value. 197 if strings.Contains(layout, ":") { 198 return errors.Wrap(fmt.Errorf("path contains ':' character: '%s'", layout), "invalid --layout") 199 } 200 if layout == "" { 201 return errors.Wrap(fmt.Errorf("path is empty"), "invalid --layout") 202 } 203 204 ctx.App.Metadata["--image-path"] = layout 205 } 206 207 if oldBefore != nil { 208 return oldBefore(ctx) 209 } 210 return nil 211 } 212 213 return cmd 214 } 215 216 func uxRemap(cmd cli.Command) cli.Command { 217 cmd.Flags = append(cmd.Flags, []cli.Flag{ 218 cli.StringSliceFlag{ 219 Name: "uid-map", 220 Usage: "specifies a uid mapping to use (container:host:size)", 221 }, 222 cli.StringSliceFlag{ 223 Name: "gid-map", 224 Usage: "specifies a gid mapping to use (container:host:size)", 225 }, 226 cli.BoolFlag{ 227 Name: "rootless", 228 Usage: "enable rootless command support", 229 }, 230 }...) 231 232 return cmd 233 }