From 7a536e8d663b9022f748317b748ee97a26639c03 Mon Sep 17 00:00:00 2001 From: Tino Rusch Date: Wed, 31 May 2017 09:33:54 +0200 Subject: [PATCH 1/4] stage0: add --expose flag on app level This allows us to specify exposed ports at runtime in favor of patching the image manifests. A typical call will look like this: rkt run \ --port shell:11111 \ trusch.io/alpine \ --expose shell,port=12345,protocol=tcp \ --exec nc -- -lk -p 12345 -e /bin/sh This fixes issue #2113 and is conflicting with PR #3407 --- common/apps/apps.go | 1 + rkt/cli_apps.go | 30 ++++++++++++++++++++++++++++++ rkt/run.go | 3 ++- stage0/common.go | 2 +- stage0/run.go | 18 ++++++++++++++++++ 5 files changed, 52 insertions(+), 2 deletions(-) diff --git a/common/apps/apps.go b/common/apps/apps.go index 598a6de326..73dd857033 100644 --- a/common/apps/apps.go +++ b/common/apps/apps.go @@ -54,6 +54,7 @@ type App struct { WorkingDir string // working directory override for image ReadOnlyRootFS bool // read-only rootfs override. Mounts []schema.Mount // mounts for this app (superseding any mounts in rktApps.mounts of same MountPoint) + Ports []types.Port // ports for this app to be exposed MemoryLimit *types.ResourceMemory // memory isolator override CPULimit *types.ResourceCPU // cpu isolator override CPUShares *types.LinuxCPUShares // cpu-shares isolator override diff --git a/rkt/cli_apps.go b/rkt/cli_apps.go index b41c3e283f..4da2218426 100644 --- a/rkt/cli_apps.go +++ b/rkt/cli_apps.go @@ -17,6 +17,7 @@ package main import ( + "encoding/json" "fmt" "net/url" "strconv" @@ -895,3 +896,32 @@ func (au *appStderr) String() string { func (au *appStderr) Type() string { return "appStderr" } + +// appExpose is for --expose flags in the form of: --expose=query,protocol=tcp,port=8080,count=1,socketActivated=true +type appExpose apps.Apps + +func (ae *appExpose) Set(s string) error { + port, err := types.PortFromString(s) + if err != nil { + return err + } + app := (*apps.Apps)(ae).Last() + if app == nil { + return fmt.Errorf("--expose must follow an application") + } + app.Ports = append(app.Ports, *port) + return nil +} + +func (ae *appExpose) String() string { + app := (*apps.Apps)(ae).Last() + if app == nil { + return "" + } + bs, _ := json.Marshal(app.Ports) + return string(bs) +} + +func (ae *appExpose) Type() string { + return "appExpose" +} diff --git a/rkt/run.go b/rkt/run.go index 9fe9e7b1aa..00ed1a6a82 100644 --- a/rkt/run.go +++ b/rkt/run.go @@ -103,6 +103,7 @@ func addAppFlags(cmd *cobra.Command) { cmd.Flags().Var((*appWorkingDir)(&rktApps), "working-dir", "override the working directory of the preceding image") cmd.Flags().Var((*appReadOnlyRootFS)(&rktApps), "readonly-rootfs", "if set, the app's rootfs will be mounted read-only") cmd.Flags().Var((*appMount)(&rktApps), "mount", "mount point binding a volume to a path within an app") + cmd.Flags().Var((*appExpose)(&rktApps), "expose", "expose ports of the application (example: '--expose=http,protocol=tcp,port=8080')") cmd.Flags().Var((*appUser)(&rktApps), "user", "user override for the preceding image (example: '--user=user')") cmd.Flags().Var((*appGroup)(&rktApps), "group", "group override for the preceding image (example: '--group=group')") cmd.Flags().Var((*appSupplementaryGIDs)(&rktApps), "supplementary-gids", "supplementary group IDs override for the preceding image (examples: '--supplementary-gids=1024,2048'") @@ -335,7 +336,6 @@ func runRun(cmd *cobra.Command, args []string) (exit int) { pcfg.EnvFromFile = flagEnvFromFile.Strings() pcfg.Apps = &rktApps } - if globalFlags.Debug { stage0.InitDebug() } @@ -402,6 +402,7 @@ func runRun(cmd *cobra.Command, args []string) (exit int) { return 254 } + if len(manifest.Apps) == 0 { stderr.Print("pod must contain at least one application") return 254 diff --git a/stage0/common.go b/stage0/common.go index 9a13791853..63733eba13 100644 --- a/stage0/common.go +++ b/stage0/common.go @@ -111,7 +111,6 @@ func (ce CrossingEntrypoint) Run() error { // generateRuntimeApp merges runtime information from the image manifest and from // runtime configuration overrides, returning a full configuration for a runtime app func generateRuntimeApp(appRunConfig *apps.App, am *schema.ImageManifest, podMounts []schema.Mount) (schema.RuntimeApp, error) { - ra := schema.RuntimeApp{ App: am.App, Image: schema.RuntimeImage{ @@ -122,6 +121,7 @@ func generateRuntimeApp(appRunConfig *apps.App, am *schema.ImageManifest, podMou Mounts: MergeMounts(podMounts, appRunConfig.Mounts), ReadOnlyRootFS: appRunConfig.ReadOnlyRootFS, } + ra.App.Ports = MergePorts(ra.App.Ports, appRunConfig.Ports) appName, err := types.NewACName(appRunConfig.Name) if err != nil { diff --git a/stage0/run.go b/stage0/run.go index a4d4d8c6a0..e315e924e6 100644 --- a/stage0/run.go +++ b/stage0/run.go @@ -188,6 +188,24 @@ func MergeMounts(mounts []schema.Mount, appMounts []schema.Mount) []schema.Mount return deduplicateMPs(ml) } +// MergePorts merges two port lists +func MergePorts(a, b []types.Port) []types.Port { + m := make(map[types.ACName]types.Port) + for _,port := range a { + m[port.Name] = port + } + for _,port := range b { + m[port.Name] = port + } + res := make([]types.Port, len(m)) + i := 0 + for _,port := range m { + res[i] = port + i++ + } + return res +} + // generatePodManifest creates the pod manifest from the command line input. // It returns the pod manifest as []byte on success. // This is invoked if no pod manifest is specified at the command line. From 6947674437d0b5b7f14830eb4aa9bd1fe4e7acec Mon Sep 17 00:00:00 2001 From: Tino Rusch Date: Wed, 31 May 2017 10:01:09 +0200 Subject: [PATCH 2/4] stage0: applied gofmt on --expose changes; --- rkt/run.go | 1 - stage0/run.go | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/rkt/run.go b/rkt/run.go index 00ed1a6a82..4f4909cc6f 100644 --- a/rkt/run.go +++ b/rkt/run.go @@ -402,7 +402,6 @@ func runRun(cmd *cobra.Command, args []string) (exit int) { return 254 } - if len(manifest.Apps) == 0 { stderr.Print("pod must contain at least one application") return 254 diff --git a/stage0/run.go b/stage0/run.go index e315e924e6..e924e0fd97 100644 --- a/stage0/run.go +++ b/stage0/run.go @@ -191,15 +191,15 @@ func MergeMounts(mounts []schema.Mount, appMounts []schema.Mount) []schema.Mount // MergePorts merges two port lists func MergePorts(a, b []types.Port) []types.Port { m := make(map[types.ACName]types.Port) - for _,port := range a { + for _, port := range a { m[port.Name] = port } - for _,port := range b { + for _, port := range b { m[port.Name] = port } res := make([]types.Port, len(m)) i := 0 - for _,port := range m { + for _, port := range m { res[i] = port i++ } From b8b874788285d0734375ea6a03fa2a3fc9a28115 Mon Sep 17 00:00:00 2001 From: Tino Rusch Date: Thu, 1 Jun 2017 08:25:16 +0200 Subject: [PATCH 3/4] stage0: --expose flag: check for null pointers before merging port lists; --- stage0/common.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stage0/common.go b/stage0/common.go index 63733eba13..b396e0de51 100644 --- a/stage0/common.go +++ b/stage0/common.go @@ -121,7 +121,9 @@ func generateRuntimeApp(appRunConfig *apps.App, am *schema.ImageManifest, podMou Mounts: MergeMounts(podMounts, appRunConfig.Mounts), ReadOnlyRootFS: appRunConfig.ReadOnlyRootFS, } - ra.App.Ports = MergePorts(ra.App.Ports, appRunConfig.Ports) + if ra.App != nil && appRunConfig != nil { + ra.App.Ports = MergePorts(ra.App.Ports, appRunConfig.Ports) + } appName, err := types.NewACName(appRunConfig.Name) if err != nil { From 349f89921bae11091a4369ca6f5e06d71bc8b102 Mon Sep 17 00:00:00 2001 From: Tino Rusch Date: Thu, 1 Jun 2017 08:26:22 +0200 Subject: [PATCH 4/4] stage0: --expose flag: assume tcp by default; --- rkt/cli_apps.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rkt/cli_apps.go b/rkt/cli_apps.go index 4da2218426..6f6bc6bdcc 100644 --- a/rkt/cli_apps.go +++ b/rkt/cli_apps.go @@ -905,6 +905,9 @@ func (ae *appExpose) Set(s string) error { if err != nil { return err } + if port.Protocol == "" { + port.Protocol = "tcp" + } app := (*apps.Apps)(ae).Last() if app == nil { return fmt.Errorf("--expose must follow an application")