@@ -20,6 +20,8 @@ package main
2020import (
2121 "fmt"
2222 "os"
23+ "regexp"
24+ "strings"
2325
2426 "github.com/Sirupsen/logrus"
2527 "github.com/urfave/cli"
@@ -33,6 +35,9 @@ var version = ""
3335// populated on build by make.
3436var gitCommit = ""
3537
38+ // refRegexp defines the regexp that a given OCI tag must obey.
39+ var refRegexp = regexp .MustCompile (`^([A-Za-z0-9._-]+)+$` )
40+
3641const (
3742 usage = `umoci modifies Open Container images`
3843)
@@ -83,6 +88,103 @@ func main() {
8388 statCommand ,
8489 }
8590
91+ app .Metadata = map [string ]interface {}{}
92+
93+ // In order to consolidate a lot of the --image and --layout handling, we
94+ // have to do some monkey-patching of commands. In particular, we set up
95+ // the --image and --layout flags (for image and layout category commands)
96+ // and then add parsing code to cmd.Before so that we can validate and
97+ // parse the required arguments. It's definitely not pretty, but it's the
98+ // best we can do without making them all global flags and then having odd
99+ // semantics.
100+
101+ for idx , cmd := range app .Commands {
102+ var flag cli.Flag
103+ oldBefore := cmd .Before
104+
105+ switch cmd .Category {
106+ case "image" :
107+ // Does the command modify images (manifests)?
108+ flag = cli.StringFlag {
109+ Name : "image" ,
110+ Usage : "OCI image URI of the form 'path[:tag]'" ,
111+ }
112+
113+ // Add BeforeFunc that will verify code.
114+ cmd .Before = func (ctx * cli.Context ) error {
115+ // Parse --image.
116+ image := ctx .String ("image" )
117+
118+ var dir , tag string
119+ sep := strings .LastIndex (image , ":" )
120+ if sep == - 1 {
121+ dir = image
122+ tag = "latest"
123+ } else {
124+ dir = image [:sep ]
125+ tag = image [sep + 1 :]
126+ }
127+
128+ // Verify directory value.
129+ if strings .Contains (dir , ":" ) {
130+ return fmt .Errorf ("invalid --image: directory contains ':' character: '%s'" , dir )
131+ }
132+ if dir == "" {
133+ return fmt .Errorf ("invalid --image: directory is empty" )
134+ }
135+
136+ // Verify tag value.
137+ if ! refRegexp .MatchString (tag ) {
138+ return fmt .Errorf ("invalid --image: tag contains invalid characters: '%s'" , tag )
139+ }
140+ if tag == "" {
141+ return fmt .Errorf ("invalid --image: tag is empty" )
142+ }
143+
144+ ctx .App .Metadata ["layout" ] = dir
145+ ctx .App .Metadata ["tag" ] = tag
146+
147+ if oldBefore != nil {
148+ return oldBefore (ctx )
149+ }
150+ return nil
151+ }
152+
153+ case "layout" :
154+ // Does the command modify an OCI image layout itself?
155+ flag = cli.StringFlag {
156+ Name : "layout" ,
157+ Usage : "OCI image URI of the form 'path'" ,
158+ }
159+
160+ // Add BeforeFunc that will verify code.
161+ cmd .Before = func (ctx * cli.Context ) error {
162+ dir := ctx .String ("layout" )
163+ // Verify directory value.
164+ if strings .Contains (dir , ":" ) {
165+ return fmt .Errorf ("invalid --layout: directory contains ':' character: '%s'" , dir )
166+ }
167+ if dir == "" {
168+ return fmt .Errorf ("invalid --layout: directory is empty" )
169+ }
170+
171+ ctx .App .Metadata ["layout" ] = dir
172+
173+ if oldBefore != nil {
174+ return oldBefore (ctx )
175+ }
176+ return nil
177+ }
178+ default :
179+ // This is a programming error. All umoci commands should fall into
180+ // one of the above categories.
181+ panic ("Unknown command category: " + cmd .Category )
182+ }
183+
184+ cmd .Flags = append ([]cli.Flag {flag }, cmd .Flags ... )
185+ app .Commands [idx ] = cmd
186+ }
187+
86188 // Actually run umoci.
87189 if err := app .Run (os .Args ); err != nil {
88190 logrus .Fatal (err )
0 commit comments