diff --git a/common/apps/app.go b/common/apps/app.go new file mode 100644 index 0000000..7b08384 --- /dev/null +++ b/common/apps/app.go @@ -0,0 +1,102 @@ +// Copyright 2015 CoreOS, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//+build linux + +package apps + +import ( + "github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/schema/types" +) + +type App struct { + Image string // the image reference as supplied by the user on the cli + Args []string // any arguments the user supplied for this app + Asc string // signature file override for image verification (if fetching occurs) + + // TODO(jonboulle): These images are partially-populated hashes, this should be clarified. + ImageID types.Hash // resolved image identifier +} + +type Apps struct { + apps []App +} + +// Reset creates a new slice for al.apps, needed by tests +func (al *Apps) Reset() { + al.apps = make([]App, 0) +} + +// Count returns the number of apps in al +func (al *Apps) Count() int { + return len(al.apps) +} + +// Create creates a new app in al and returns a pointer to it +func (al *Apps) Create(img string) { + al.apps = append(al.apps, App{Image: img}) +} + +// Last returns a pointer to the top app in al +func (al *Apps) Last() *App { + if len(al.apps) == 0 { + return nil + } + return &al.apps[len(al.apps)-1] +} + +// Walk iterates on al.apps calling f for each app +// walking stops if f returns an error, the error is simply returned +func (al *Apps) Walk(f func(*App) error) error { + for i, _ := range al.apps { + // XXX(vc): note we supply f() with a pointer to the app instance in al.apps to enable modification by f() + if err := f(&al.apps[i]); err != nil { + return err + } + } + return nil +} + +// these convenience functions just return typed lists containing just the named member +// TODO(vc): these probably go away when we just pass Apps to stage0 + +// GetImages returns a list of the images in al, one per app. +// The order reflects the app order in al. +func (al *Apps) GetImages() []string { + il := []string{} + for _, a := range al.apps { + il = append(il, a.Image) + } + return il +} + +// GetArgs returns a list of lists of arguments in al, one list of args per app. +// The order reflects the app order in al. +func (al *Apps) GetArgs() [][]string { + aal := [][]string{} + for _, a := range al.apps { + aal = append(aal, a.Args) + } + return aal +} + +// GetImageIDs returns a list of the imageIDs in al, one per app. +// The order reflects the app order in al. +func (al *Apps) GetImageIDs() []types.Hash { + hl := []types.Hash{} + for _, a := range al.apps { + hl = append(hl, a.ImageID) + } + return hl +} diff --git a/rkt/app.go b/rkt/cli_apps.go similarity index 52% rename from rkt/app.go rename to rkt/cli_apps.go index 8ae9e42..4c19dc8 100644 --- a/rkt/app.go +++ b/rkt/cli_apps.go @@ -20,71 +20,18 @@ import ( "flag" "fmt" - "github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/schema/types" + "github.com/coreos/rkt/common/apps" ) var ( - Apps rktApps // global apps + rktApps apps.Apps // global used by run/prepare for representing the apps expressed via the cli ) -type rktApp struct { - image string // the image reference as supplied by the user on the cli - args []string // any arguments the user supplied for this app - asc string // signature file override for image verification (if fetching occurs) - - imageID types.Hash // resolved image identifier -} - -// this needs to be a struct for the type defines like appAsc to work correctly with receivers -type rktApps struct { - apps []rktApp -} - -// reset creates a new slice for al.apps, needed by tests -func (al *rktApps) reset() { - al.apps = make([]rktApp, 0) -} - -// count returns the number of apps in al -func (al *rktApps) count() int { - return len(al.apps) -} - -// create creates a new app in al and returns a pointer to it -func (al *rktApps) create(img string) { - al.apps = append(al.apps, rktApp{image: img}) -} - -// last returns a pointer to the top app in al -func (al *rktApps) last() *rktApp { - if len(al.apps) == 0 { - return nil - } else { - return &al.apps[len(al.apps)-1] - } -} - -// appendArg appends another argument onto the app -func (a *rktApp) appendArg(arg string) { - a.args = append(a.args, arg) -} - -// sets the ascii-armored signature file path for the app -func (a *rktApp) setAsc(asc string) { - a.asc = asc -} - -// gets the ascii-armored signature file path for the app -func (a *rktApp) getAsc() string { - return a.asc -} - // parseApps looks through the args for support of per-app argument lists delimited with "--" and "---". // Between per-app argument lists flags.Parse() is called using the supplied FlagSet. // Anything not consumed by flags.Parse() and not found to be a per-app argument list is treated as an image. -func (al *rktApps) parse(args []string, flags *flag.FlagSet) error { - al.reset() - nAppsLastAppArgs := al.count() +func parseApps(al *apps.Apps, args []string, flags *flag.FlagSet) error { + nAppsLastAppArgs := al.Count() // valid args here may either be: // not-"--"; flags handled by *flags or an image specifier @@ -101,7 +48,8 @@ func (al *rktApps) parse(args []string, flags *flag.FlagSet) error { inAppArgs = false default: // keep appending to this app's args - al.last().appendArg(a) + app := al.Last() + app.Args = append(app.Args, a) } } else { switch a { @@ -110,13 +58,13 @@ func (al *rktApps) parse(args []string, flags *flag.FlagSet) error { inAppArgs = true // catch some likely mistakes - if nAppsLastAppArgs == al.count() { - if al.count() == 0 { + if nAppsLastAppArgs == al.Count() { + if al.Count() == 0 { return fmt.Errorf("an image is required before any app arguments") } return fmt.Errorf("only one set of app arguments allowed per image") } - nAppsLastAppArgs = al.count() + nAppsLastAppArgs = al.Count() case "---": // ignore triple dashes since they aren't images // TODO(vc): I don't think ignoring this is appropriate, probably should error; it implies malformed argv. @@ -139,7 +87,7 @@ func (al *rktApps) parse(args []string, flags *flag.FlagSet) error { i += nInterFlags - 1 // - 1 because of i++ } else { // flags.Parse() didn't want this arg, treat as image - al.create(a) + al.Create(a) } } } @@ -148,65 +96,30 @@ func (al *rktApps) parse(args []string, flags *flag.FlagSet) error { return nil } -// these convenience functions just return typed lists containing just the named member -// TODO(vc): these probably go away when we just pass rktApps to stage0 - -// getImages returns a list of the images in al, one per app. -// The order reflects the app order in al. -func (al *rktApps) getImages() []string { - il := []string{} - for _, a := range al.apps { - il = append(il, a.image) - } - return il -} - -// getArgs returns a list of lists of arguments in al, one list of args per app. -// The order reflects the app order in al. -func (al *rktApps) getArgs() [][]string { - aal := [][]string{} - for _, a := range al.apps { - aal = append(aal, a.args) - } - return aal -} - -// getImageIDs returns a list of the imageIDs in al, one per app. -// The order reflects the app order in al. -func (al *rktApps) getImageIDs() []types.Hash { - hl := []types.Hash{} - for _, a := range al.apps { - hl = append(hl, a.imageID) - } - return hl -} - // Value interface implementations for the various per-app fields we provide flags for // appAsc is for aci --signature overrides -type appAsc rktApps +type appAsc apps.Apps func (al *appAsc) Set(s string) error { - app := (*rktApps)(al).last() + app := (*apps.Apps)(al).Last() if app == nil { return fmt.Errorf("--signature must follow an image") } - - if app.getAsc() != "" { + if app.Asc != "" { return fmt.Errorf("--signature specified multiple times for the same image") } - - app.setAsc(s) + app.Asc = s return nil } func (al *appAsc) String() string { - app := (*rktApps)(al).last() + app := (*apps.Apps)(al).Last() if app == nil { return "" } - return app.getAsc() + return app.Asc } // TODO(vc): --mount, --set-env, etc. diff --git a/rkt/app_test.go b/rkt/cli_apps_test.go similarity index 93% rename from rkt/app_test.go rename to rkt/cli_apps_test.go index f791283..19a8c92 100644 --- a/rkt/app_test.go +++ b/rkt/cli_apps_test.go @@ -62,9 +62,10 @@ func TestParseAppArgs(t *testing.T) { } for i, tt := range tests { - err := Apps.parse(strings.Split(tt.in, " "), flags) - ga := Apps.getArgs() - gi := Apps.getImages() + rktApps.Reset() + err := parseApps(&rktApps, strings.Split(tt.in, " "), flags) + ga := rktApps.GetArgs() + gi := rktApps.GetImages() if gerr := (err != nil); gerr != tt.werr { t.Errorf("#%d: err==%v, want errstate %t", i, err, tt.werr) } diff --git a/rkt/image.go b/rkt/image.go index ef7cc7e..08f13df 100644 --- a/rkt/image.go +++ b/rkt/image.go @@ -21,20 +21,20 @@ import ( "github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/schema/types" "github.com/coreos/rkt/cas" + "github.com/coreos/rkt/common/apps" "github.com/coreos/rkt/pkg/keystore" ) // findImages uses findImage to attain a list of image hashes using discovery if necessary -func (al *rktApps) findImages(ds *cas.Store, ks *keystore.Keystore) error { - for _, app := range al.apps { - h, err := findImage(app.image, app.asc, ds, ks, true) +func findImages(al *apps.Apps, ds *cas.Store, ks *keystore.Keystore) error { + return al.Walk(func(app *apps.App) error { + h, err := findImage(app.Image, app.Asc, ds, ks, true) if err != nil { return err } - app.imageID = *h - } - - return nil + app.ImageID = *h + return nil + }) } // findImage will recognize a ACI hash and use that, import a local file, use diff --git a/rkt/prepare.go b/rkt/prepare.go index 7ac5b80..7902709 100644 --- a/rkt/prepare.go +++ b/rkt/prepare.go @@ -67,12 +67,12 @@ func runPrepare(args []string) (exit int) { } } - if err = Apps.parse(args, &prepareFlags); err != nil { + if err = parseApps(&rktApps, args, &prepareFlags); err != nil { stderr("prepare: error parsing app image arguments: %v", err) return 1 } - if Apps.count() < 1 { + if rktApps.Count() < 1 { stderr("prepare: Must provide at least one image") return 1 } @@ -97,7 +97,7 @@ func runPrepare(args []string) (exit int) { return 1 } - if err := Apps.findImages(ds, getKeystore()); err != nil { + if err := findImages(&rktApps, ds, getKeystore()); err != nil { stderr("%v", err) return 1 } @@ -114,9 +114,9 @@ func runPrepare(args []string) (exit int) { Debug: globalFlags.Debug, Stage1Image: *s1img, UUID: p.uuid, - Images: Apps.getImageIDs(), + Images: rktApps.GetImageIDs(), }, - ExecAppends: Apps.getArgs(), + ExecAppends: rktApps.GetArgs(), Volumes: []types.Volume(flagVolumes), InheritEnv: flagInheritEnv, ExplicitEnv: flagExplicitEnv.Strings(), diff --git a/rkt/run.go b/rkt/run.go index 333b2a1..9a247ef 100644 --- a/rkt/run.go +++ b/rkt/run.go @@ -78,7 +78,7 @@ func init() { runFlags.BoolVar(&flagNoOverlay, "no-overlay", false, "disable overlay filesystem") runFlags.Var(&flagExplicitEnv, "set-env", "an environment variable to set for apps in the form name=value") runFlags.BoolVar(&flagInteractive, "interactive", false, "run pod interactively") - runFlags.Var((*appAsc)(&Apps), "signature", "local signature file to use in validating the preceding image") + runFlags.Var((*appAsc)(&rktApps), "signature", "local signature file to use in validating the preceding image") flagVolumes = volumeList{} } @@ -97,13 +97,13 @@ func runRun(args []string) (exit int) { } } - err := Apps.parse(args, &runFlags) + err := parseApps(&rktApps, args, &runFlags) if err != nil { stderr("run: error parsing app image arguments: %v", err) return 1 } - if Apps.count() < 1 { + if rktApps.Count() < 1 { stderr("run: must provide at least one image") return 1 } @@ -120,7 +120,7 @@ func runRun(args []string) (exit int) { return 1 } - if err := Apps.findImages(ds, getKeystore()); err != nil { + if err := findImages(&rktApps, ds, getKeystore()); err != nil { stderr("%v", err) return 1 } @@ -135,13 +135,13 @@ func runRun(args []string) (exit int) { Store: ds, Stage1Image: *s1img, UUID: p.uuid, - Images: Apps.getImageIDs(), + Images: rktApps.GetImageIDs(), Debug: globalFlags.Debug, } pcfg := stage0.PrepareConfig{ CommonConfig: cfg, - ExecAppends: Apps.getArgs(), + ExecAppends: rktApps.GetArgs(), Volumes: []types.Volume(flagVolumes), InheritEnv: flagInheritEnv, ExplicitEnv: flagExplicitEnv.Strings(),