diff --git a/docs/website/docs/overview/configure.md b/docs/website/docs/overview/configure.md index 331f63f80fd..b2b110b9d99 100644 --- a/docs/website/docs/overview/configure.md +++ b/docs/website/docs/overview/configure.md @@ -190,6 +190,7 @@ Options here are mostly used for debugging and testing `odo` behavior. | `DEVFILE_PROXY` | Integration tests will use this address as Devfile registry instead of `registry.stage.devfile.io` | v3.0.0-beta3 | `my-registry.example.com` | | `TELEMETRY_CALLER` | Caller identifier passed to [telemetry](https://github.com/redhat-developer/odo/blob/main/USAGE_DATA.md). Case-insensitive. Acceptable values: `vscode`, `intellij`, `jboss`. | v3.1.0 | `intellij` | | `ODO_TRACKING_CONSENT` | Useful for controlling [telemetry](https://github.com/redhat-developer/odo/blob/main/USAGE_DATA.md). Acceptable values: `yes` ([enables telemetry](https://github.com/redhat-developer/odo/blob/main/USAGE_DATA.md) and skips consent prompt), `no` (disables telemetry and consent prompt). Takes precedence over the [`ConsentTelemetry`](#preference-key-table) preference. | v3.2.0 | `yes` | -| `ODO_EXPERIMENTAL_MODE` | Whether to enable experimental features. See [Experimental Mode](../user-guides/advanced/experimental-mode) for more details. Acceptable values: boolean values(1) | v3.3.0 | `true` | +| `ODO_EXPERIMENTAL_MODE` | Whether to enable experimental features. See [Experimental Mode](../user-guides/advanced/experimental-mode) for more details. Acceptable values: boolean values(1) | v3.3.0 | `true` | +| `ODO_PUSH_IMAGES` | Whether to push the images once built; this is used only when applying Devfile image components as part of a Dev Session running on Podman; this is useful for integration tests running on Podman. `true` by default | v3.7.0 | `false` | (1) Accepted boolean values are: `1`, `t`, `T`, `TRUE`, `true`, `True`, `0`, `f`, `F`, `FALSE`, `false`, `False`. \ No newline at end of file diff --git a/pkg/config/config.go b/pkg/config/config.go index de086fd585a..a329d28b230 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -17,6 +17,7 @@ type Configuration struct { PodmanCmd string `env:"PODMAN_CMD,default=podman"` TelemetryCaller string `env:"TELEMETRY_CALLER,default="` OdoExperimentalMode bool `env:"ODO_EXPERIMENTAL_MODE,default=false"` + PushImages bool `env:"ODO_PUSH_IMAGES,default=true"` } // GetConfiguration initializes a Configuration for odo by using the system environment. diff --git a/pkg/dev/podmandev/command.go b/pkg/dev/podmandev/command.go index 053edd14a65..9e5c183c16f 100644 --- a/pkg/dev/podmandev/command.go +++ b/pkg/dev/podmandev/command.go @@ -1,17 +1,24 @@ package podmandev import ( + "context" + devfilev1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" "k8s.io/klog" "github.com/redhat-developer/odo/pkg/component" + envcontext "github.com/redhat-developer/odo/pkg/config/context" + "github.com/redhat-developer/odo/pkg/devfile/image" "github.com/redhat-developer/odo/pkg/exec" "github.com/redhat-developer/odo/pkg/libdevfile" "github.com/redhat-developer/odo/pkg/log" "github.com/redhat-developer/odo/pkg/platform" + "github.com/redhat-developer/odo/pkg/testingutil/filesystem" ) type commandHandler struct { + ctx context.Context + fs filesystem.Filesystem execClient exec.Client platformClient platform.Client componentExists bool @@ -23,9 +30,7 @@ type commandHandler struct { var _ libdevfile.Handler = (*commandHandler)(nil) func (a commandHandler) ApplyImage(img devfilev1.Component) error { - klog.V(4).Info("apply image commands are not implemented on podman") - log.Warningf("Apply Image commands are not implemented on Podman. Skipping: %v", img.Name) - return nil + return image.BuildPushSpecificImage(a.ctx, a.fs, img, envcontext.GetEnvConfig(a.ctx).PushImages) } func (a commandHandler) ApplyKubernetes(kubernetes devfilev1.Component) error { diff --git a/pkg/dev/podmandev/podmandev.go b/pkg/dev/podmandev/podmandev.go index b887dec7d59..875b4c55f68 100644 --- a/pkg/dev/podmandev/podmandev.go +++ b/pkg/dev/podmandev/podmandev.go @@ -25,6 +25,7 @@ import ( "github.com/redhat-developer/odo/pkg/podman" "github.com/redhat-developer/odo/pkg/state" "github.com/redhat-developer/odo/pkg/sync" + "github.com/redhat-developer/odo/pkg/testingutil/filesystem" "github.com/redhat-developer/odo/pkg/watch" corev1 "k8s.io/api/core/v1" @@ -38,6 +39,8 @@ const ( ) type DevClient struct { + fs filesystem.Filesystem + podmanClient podman.Client syncClient sync.Client execClient exec.Client @@ -51,6 +54,7 @@ type DevClient struct { var _ dev.Client = (*DevClient)(nil) func NewDevClient( + fs filesystem.Filesystem, podmanClient podman.Client, syncClient sync.Client, execClient exec.Client, @@ -58,6 +62,7 @@ func NewDevClient( watchClient watch.Client, ) *DevClient { return &DevClient{ + fs: fs, podmanClient: podmanClient, syncClient: syncClient, execClient: execClient, diff --git a/pkg/dev/podmandev/reconcile.go b/pkg/dev/podmandev/reconcile.go index dc59ec2db32..0d6511d0b7b 100644 --- a/pkg/dev/podmandev/reconcile.go +++ b/pkg/dev/podmandev/reconcile.go @@ -97,6 +97,8 @@ func (o *DevClient) reconcile( cmdName = options.DebugCommand } cmdHandler := commandHandler{ + ctx: ctx, + fs: o.fs, execClient: o.execClient, platformClient: o.podmanClient, componentExists: componentStatus.RunExecuted, diff --git a/pkg/devfile/image/image.go b/pkg/devfile/image/image.go index b23b0c92b12..8a09203c8b6 100644 --- a/pkg/devfile/image/image.go +++ b/pkg/devfile/image/image.go @@ -83,7 +83,13 @@ func buildPushImage(backend Backend, fs filesystem.Filesystem, image *devfile.Im if image == nil { return errors.New("image should not be nil") } - log.Sectionf("Building & Pushing Container: %s", image.ImageName) + var msg string + if push { + msg = "Building & Pushing Image: %s" + } else { + msg = "Building Image: %s" + } + log.Sectionf(msg, image.ImageName) err := backend.Build(fs, image, devfilePath) if err != nil { return err diff --git a/pkg/odo/genericclioptions/clientset/clientset.go b/pkg/odo/genericclioptions/clientset/clientset.go index f1d44e0cc95..160e2d2e5f0 100644 --- a/pkg/odo/genericclioptions/clientset/clientset.go +++ b/pkg/odo/genericclioptions/clientset/clientset.go @@ -242,6 +242,7 @@ func Fetch(command *cobra.Command, platform string) (*Clientset, error) { switch platform { case commonflags.PlatformPodman: dep.DevClient = podmandev.NewDevClient( + dep.FS, dep.PodmanClient, dep.SyncClient, dep.ExecClient, diff --git a/tests/helper/component_podman.go b/tests/helper/component_podman.go index 23767d82674..08b5754e0f4 100644 --- a/tests/helper/component_podman.go +++ b/tests/helper/component_podman.go @@ -141,3 +141,15 @@ func (o *PodmanComponent) GetPodLogs() string { }) return string(stdout) } + +func (o *PodmanComponent) ListImages() string { + cmd := exec.Command("podman", "images", "--format", "{{.Repository}}:{{.Tag}}", "--noheading") + stdout, err := cmd.Output() + Expect(err).ToNot(HaveOccurred(), func() { + if exiterr, ok := err.(*exec.ExitError); ok { + err = fmt.Errorf("%s: %s", err, string(exiterr.Stderr)) + } + fmt.Fprintln(GinkgoWriter, err) + }) + return string(stdout) +} diff --git a/tests/integration/cmd_dev_debug_test.go b/tests/integration/cmd_dev_debug_test.go index 5a58a50fb39..c34a28d4046 100644 --- a/tests/integration/cmd_dev_debug_test.go +++ b/tests/integration/cmd_dev_debug_test.go @@ -246,7 +246,7 @@ var _ = Describe("odo dev debug command tests", func() { helper.MatchAllInOutput(string(out), deploymentNames) } checkImageBuilt := func() { - Expect(string(sessionOut)).To(ContainSubstring("Building & Pushing Container")) + Expect(string(sessionOut)).To(ContainSubstring("Building & Pushing Image")) Expect(string(sessionOut)).To(ContainSubstring("build -t quay.io/unknown-account/myimage -f " + filepath.Join(commonVar.Context, "Dockerfile ") + commonVar.Context)) Expect(string(sessionOut)).To(ContainSubstring("push quay.io/unknown-account/myimage")) } diff --git a/tests/integration/cmd_dev_test.go b/tests/integration/cmd_dev_test.go index c3b39419996..7aba3bcfb53 100644 --- a/tests/integration/cmd_dev_test.go +++ b/tests/integration/cmd_dev_test.go @@ -2923,41 +2923,48 @@ CMD ["npm", "start"] }) })) } - for _, ctx := range []struct { - title string - resources, args []string - }{ - { - title: "with run command", - resources: []string{"deploy-k8s-resource", "deploy-a-third-k8s-resource", "image-build-component"}, - }, - { - title: "with debug command", - resources: []string{"deploy-another-k8s-resource", "deploy-a-third-k8s-resource", "image-build-component"}, - args: []string{"--debug"}, - }, - } { - ctx := ctx - When("using devfile that contains K8s resource to run it on podman", Label(helper.LabelPodman), func() { - BeforeEach(func() { - helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context) - helper.CopyExampleDevFile( - filepath.Join("source", "devfiles", "nodejs", "devfile-composite-apply-different-commandgk.yaml"), - filepath.Join(commonVar.Context, "devfile.yaml"), - helper.DevfileMetadataNameSetter(cmpName)) - }) - It(fmt.Sprintf("should show warning about being unable to create the resource when running odo dev %s on podman", ctx.title), func() { - err := helper.RunDevMode(helper.DevSessionOpts{RunOnPodman: true, CmdlineArgs: ctx.args}, func(session *gexec.Session, outContents, errContents []byte, ports map[string]string) { - Expect(string(errContents)).To(ContainSubstring("Kubernetes components are not supported on Podman. Skipping: ")) - Expect(string(errContents)).To(ContainSubstring("Apply Kubernetes components are not supported on Podman. Skipping: ")) - Expect(string(errContents)).To(ContainSubstring("Apply Image commands are not implemented on Podman. Skipping: ")) - helper.MatchAllInOutput(string(errContents), ctx.resources) - }) - Expect(err).ToNot(HaveOccurred()) - }) + + When("using devfile that contains K8s resource to run it on podman", Label(helper.LabelPodman), func() { + const ( + imgName = "quay.io/unknown-account/myimage" // hard coded from the devfile-composite-apply-different-commandgk.yaml + ) + var customImgName string + + var session helper.DevSession + var outContents, errContents []byte + BeforeEach(func() { + customImgName = fmt.Sprintf("%s:%s", imgName, cmpName) + helper.CopyExample(filepath.Join("source", "nodejs"), commonVar.Context) + helper.CopyExampleDevFile( + filepath.Join("source", "devfiles", "nodejs", "devfile-composite-apply-different-commandgk.yaml"), + filepath.Join(commonVar.Context, "devfile.yaml"), + helper.DevfileMetadataNameSetter(cmpName), + ) + helper.ReplaceString(filepath.Join(commonVar.Context, "devfile.yaml"), imgName, customImgName) + var err error + session, outContents, errContents, _, err = helper.StartDevMode( + helper.DevSessionOpts{RunOnPodman: true, EnvVars: []string{"ODO_PUSH_IMAGES=false"}}, + ) + Expect(err).ToNot(HaveOccurred()) + }) + AfterEach(func() { + session.Stop() + session.WaitEnd() + }) + It("should show warning about being unable to create the resource when running odo dev on podman", func() { + Expect(string(errContents)).To(ContainSubstring("Kubernetes components are not supported on Podman. Skipping: ")) + Expect(string(errContents)).To(ContainSubstring("Apply Kubernetes components are not supported on Podman. Skipping: ")) + helper.MatchAllInOutput(string(errContents), []string{"deploy-k8s-resource", "deploy-a-third-k8s-resource"}) }) - } + It("should build the images when running odo dev on podman", func() { + // we do not test push because then it becomes complex to setup image registry credentials to pull the image + // all pods created by odo have a `PullAlways` image policy. + Expect(string(outContents)).To(ContainSubstring("Building Image: %s", customImgName)) + component := helper.NewPodmanComponent(cmpName, "app") + Expect(component.ListImages()).To(ContainSubstring(customImgName)) + }) + }) for _, podman := range []bool{true, false} { podman := podman