From af2611a07308fc8ef3d196f911d02d8765503bb4 Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Thu, 28 Apr 2022 14:36:22 +0200 Subject: [PATCH 01/12] Save state (forwarded ports + PID + timestamp) to local file and cleanup --- pkg/api/component.go | 1 + pkg/odo/cli/dev/dev.go | 8 +- pkg/odo/cli/dev/writer.go | 62 ++++++- pkg/odo/cli/dev/writer_test.go | 63 +++++++ .../genericclioptions/clientset/clientset.go | 12 +- pkg/state/const.go | 3 + pkg/state/doc.go | 2 + pkg/state/interface.go | 11 ++ pkg/state/state.go | 59 +++++++ pkg/state/state_test.go | 162 ++++++++++++++++++ pkg/state/types.go | 14 ++ pkg/watch/watch.go | 8 +- 12 files changed, 395 insertions(+), 10 deletions(-) create mode 100644 pkg/odo/cli/dev/writer_test.go create mode 100644 pkg/state/const.go create mode 100644 pkg/state/doc.go create mode 100644 pkg/state/interface.go create mode 100644 pkg/state/state.go create mode 100644 pkg/state/state_test.go create mode 100644 pkg/state/types.go diff --git a/pkg/api/component.go b/pkg/api/component.go index a1e563f2753..4ad4d62215c 100644 --- a/pkg/api/component.go +++ b/pkg/api/component.go @@ -19,6 +19,7 @@ type Component struct { type ForwardedPort struct { ContainerName string `json:"containerName"` + LocalAddress string `json:"localAddress"` LocalPort int `json:"localPort"` ContainerPort int `json:"containerPort"` } diff --git a/pkg/odo/cli/dev/dev.go b/pkg/odo/cli/dev/dev.go index 48d0316555e..e4465c1ff69 100644 --- a/pkg/odo/cli/dev/dev.go +++ b/pkg/odo/cli/dev/dev.go @@ -224,7 +224,7 @@ func (o *DevOptions) Run(ctx context.Context) error { // Output that the application is running, and then show the port-forwarding information log.Info("\nYour application is now running on the cluster") - portsBuf := NewPortWriter(log.GetStdout(), len(portPairsSlice)) + portsBuf := NewPortWriter(log.GetStdout(), len(portPairsSlice), ceMapping) go func() { err = o.clientset.KubernetesClient.SetupPortForwarding(pod, portPairsSlice, portsBuf, o.errOut) if err != nil { @@ -233,6 +233,10 @@ func (o *DevOptions) Run(ctx context.Context) error { }() portsBuf.Wait() + err = o.clientset.StateClient.SetForwardedPorts(portsBuf.GetForwaredPorts()) + if err != nil { + fmt.Printf("unable to save forwarded ports to state file: %v", err) + } devFileObj := o.Context.EnvSpecificInfo.GetDevfileObj() @@ -312,7 +316,7 @@ It forwards endpoints with exposure values 'public' or 'internal' to a port on l devCmd.Flags().BoolVar(&o.randomPortsFlag, "random-ports", false, "Assign random ports to redirected ports") devCmd.Flags().BoolVar(&o.debugFlag, "debug", false, "Execute the debug command within the component") - clientset.Add(devCmd, clientset.DEV, clientset.INIT, clientset.KUBERNETES) + clientset.Add(devCmd, clientset.DEV, clientset.INIT, clientset.KUBERNETES, clientset.STATE) // Add a defined annotation in order to appear in the help menu devCmd.Annotations["command"] = "main" devCmd.SetUsageTemplate(odoutil.CmdUsageTemplate) diff --git a/pkg/odo/cli/dev/writer.go b/pkg/odo/cli/dev/writer.go index 1ac552055cd..f93e6d73077 100644 --- a/pkg/odo/cli/dev/writer.go +++ b/pkg/odo/cli/dev/writer.go @@ -1,26 +1,37 @@ package dev import ( + "errors" "fmt" "io" + "regexp" + "strconv" "strings" "github.com/fatih/color" + + "github.com/redhat-developer/odo/pkg/api" + + "k8s.io/klog" ) type PortWriter struct { buffer io.Writer end chan bool len int + // mapping indicates the list of ports open by containers (ex: mapping["runtime"] = {3000, 3030}) + mapping map[string][]int + fwPorts []api.ForwardedPort } // NewPortWriter creates a writer that will write the content in buffer, // and Wait will return after strings "Forwarding from 127.0.0.1:" has been written "len" times -func NewPortWriter(buffer io.Writer, len int) *PortWriter { +func NewPortWriter(buffer io.Writer, len int, mapping map[string][]int) *PortWriter { return &PortWriter{ - buffer: buffer, - len: len, - end: make(chan bool), + buffer: buffer, + len: len, + end: make(chan bool), + mapping: mapping, } } @@ -33,6 +44,14 @@ func (o *PortWriter) Write(buf []byte) (n int, err error) { defer color.Unset() // Use it in your function s := string(buf) if strings.HasPrefix(s, "Forwarding from 127.0.0.1") { + + fwPort, err := getForwardedPort(o.mapping, s) + if err == nil { + o.fwPorts = append(o.fwPorts, fwPort) + } else { + klog.V(4).Infof("unable to get forwarded port: %v", err) + } + fmt.Fprintf(o.buffer, " - %s", s) o.len-- if o.len == 0 { @@ -45,3 +64,38 @@ func (o *PortWriter) Write(buf []byte) (n int, err error) { func (o *PortWriter) Wait() { <-o.end } + +func (o *PortWriter) GetForwaredPorts() []api.ForwardedPort { + return o.fwPorts +} + +func getForwardedPort(mapping map[string][]int, s string) (api.ForwardedPort, error) { + regex := regexp.MustCompile(`Forwarding from 127.0.0.1:([0-9]+) -> ([0-9]+)`) + matches := regex.FindStringSubmatch(s) + if len(matches) < 3 { + return api.ForwardedPort{}, errors.New("unable to analyze port forwarding string") + } + localPort, err := strconv.Atoi(matches[1]) + if err != nil { + return api.ForwardedPort{}, err + } + remotePort, err := strconv.Atoi(matches[2]) + if err != nil { + return api.ForwardedPort{}, err + } + containerName := "" + for container, ports := range mapping { + for _, port := range ports { + if port == remotePort { + containerName = container + break + } + } + } + return api.ForwardedPort{ + ContainerName: containerName, + LocalAddress: "127.0.0.1", + LocalPort: localPort, + ContainerPort: remotePort, + }, nil +} diff --git a/pkg/odo/cli/dev/writer_test.go b/pkg/odo/cli/dev/writer_test.go new file mode 100644 index 00000000000..b5dfb461e59 --- /dev/null +++ b/pkg/odo/cli/dev/writer_test.go @@ -0,0 +1,63 @@ +package dev + +import ( + "reflect" + "testing" + + "github.com/redhat-developer/odo/pkg/api" +) + +func Test_getForwardedPort(t *testing.T) { + type args struct { + mapping map[string][]int + s string + } + tests := []struct { + name string + args args + want api.ForwardedPort + wantErr bool + }{ + { + name: "find port in container", + args: args{ + mapping: map[string][]int{ + "container1": {3000, 4200}, + "container2": {80, 8080}, + }, + s: "Forwarding from 127.0.0.1:40407 -> 3000", + }, + want: api.ForwardedPort{ + ContainerName: "container1", + LocalAddress: "127.0.0.1", + LocalPort: 40407, + ContainerPort: 3000, + }, + wantErr: false, + }, + { + name: "string error", + args: args{ + mapping: map[string][]int{ + "container1": {3000, 4200}, + "container2": {80, 8080}, + }, + s: "Forwarding from 127.0.0.1:40407 => 3000", + }, + want: api.ForwardedPort{}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := getForwardedPort(tt.args.mapping, tt.args.s) + if (err != nil) != tt.wantErr { + t.Errorf("getForwardedPort() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("getForwardedPort() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/odo/genericclioptions/clientset/clientset.go b/pkg/odo/genericclioptions/clientset/clientset.go index 799b4bccd83..3758875edf4 100644 --- a/pkg/odo/genericclioptions/clientset/clientset.go +++ b/pkg/odo/genericclioptions/clientset/clientset.go @@ -14,6 +14,7 @@ package clientset import ( "github.com/redhat-developer/odo/pkg/alizer" "github.com/redhat-developer/odo/pkg/dev" + "github.com/redhat-developer/odo/pkg/state" "github.com/spf13/cobra" _delete "github.com/redhat-developer/odo/pkg/component/delete" @@ -50,6 +51,8 @@ const ( PROJECT = "DEP_PROJECT" // REGISTRY instantiates client for pkg/registry REGISTRY = "DEP_REGISTRY" + // STATE instantiates client for pkg/state + STATE = "DEP_STATE" // WATCH instantiates client for pkg/watch WATCH = "DEP_WATCH" @@ -66,7 +69,8 @@ var subdeps map[string][]string = map[string][]string{ INIT: {ALIZER, FILESYSTEM, PREFERENCE, REGISTRY}, PROJECT: {KUBERNETES_NULLABLE}, REGISTRY: {FILESYSTEM, PREFERENCE}, - WATCH: {DELETE_COMPONENT}, + STATE: {FILESYSTEM}, + WATCH: {DELETE_COMPONENT, STATE}, /* Add sub-dependencies here, if any */ } @@ -81,6 +85,7 @@ type Clientset struct { PreferenceClient preference.Client ProjectClient project.Client RegistryClient registry.Client + StateClient state.Client WatchClient watch.Client /* Add client here */ } @@ -144,8 +149,11 @@ func Fetch(command *cobra.Command) (*Clientset, error) { if isDefined(command, PROJECT) { dep.ProjectClient = project.NewClient(dep.KubernetesClient) } + if isDefined(command, STATE) { + dep.StateClient = state.NewStateClient(dep.FS) + } if isDefined(command, WATCH) { - dep.WatchClient = watch.NewWatchClient(dep.DeleteClient) + dep.WatchClient = watch.NewWatchClient(dep.DeleteClient, dep.StateClient) } if isDefined(command, DEV) { dep.DevClient = dev.NewDevClient(dep.WatchClient) diff --git a/pkg/state/const.go b/pkg/state/const.go new file mode 100644 index 00000000000..225e5fbc693 --- /dev/null +++ b/pkg/state/const.go @@ -0,0 +1,3 @@ +package state + +const _filepath = "./.odo/state.json" diff --git a/pkg/state/doc.go b/pkg/state/doc.go new file mode 100644 index 00000000000..cea421b0d98 --- /dev/null +++ b/pkg/state/doc.go @@ -0,0 +1,2 @@ +// Package state gives access to the state of the odo process stored in a local file +package state diff --git a/pkg/state/interface.go b/pkg/state/interface.go new file mode 100644 index 00000000000..aa4cf458e5f --- /dev/null +++ b/pkg/state/interface.go @@ -0,0 +1,11 @@ +package state + +import "github.com/redhat-developer/odo/pkg/api" + +type Client interface { + // SetForwardedPorts sets the forwarded ports in the state file and saves it to the file, updating the metadata + SetForwardedPorts(fwPorts []api.ForwardedPort) error + + // SaveExit resets the state file to indicate odo is not running + SaveExit() error +} diff --git a/pkg/state/state.go b/pkg/state/state.go new file mode 100644 index 00000000000..883008b394d --- /dev/null +++ b/pkg/state/state.go @@ -0,0 +1,59 @@ +package state + +import ( + "encoding/json" + "os" + "time" + + "github.com/redhat-developer/odo/pkg/api" + "github.com/redhat-developer/odo/pkg/testingutil/filesystem" +) + +type State struct { + content Content + fs filesystem.Filesystem + getSecondsFromEpoch func() int64 + getpid func() int +} + +func NewStateClient(fs filesystem.Filesystem) *State { + return &State{ + fs: fs, + getSecondsFromEpoch: getSecondsFromEpoch, + getpid: os.Getpid, + } +} + +func (o *State) SetForwardedPorts(fwPorts []api.ForwardedPort) error { + // TODO(feloy) When other data is persisted into the state file, it will be needed to read the file first + o.content.ForwardedPorts = fwPorts + o.setMetadata() + return o.save() +} + +func (o *State) SaveExit() error { + o.content.ForwardedPorts = nil + o.content.PID = 0 + o.content.Timestamp = o.getSecondsFromEpoch() + return o.save() +} + +// setMetadata sets the metadata in the state with current PID and epoch +func (o *State) setMetadata() { + o.content.PID = o.getpid() + o.content.Timestamp = o.getSecondsFromEpoch() +} + +// save writes the content structure in json format in file +func (o *State) save() error { + jsonContent, err := json.MarshalIndent(o.content, "", " ") + if err != nil { + return err + } + // .odo directory is supposed to exist, don't create it + return o.fs.WriteFile(_filepath, jsonContent, 0644) +} + +func getSecondsFromEpoch() int64 { + return time.Now().Unix() +} diff --git a/pkg/state/state_test.go b/pkg/state/state_test.go new file mode 100644 index 00000000000..0e9bdb46303 --- /dev/null +++ b/pkg/state/state_test.go @@ -0,0 +1,162 @@ +package state + +import ( + "encoding/json" + "fmt" + "reflect" + "testing" + + "github.com/redhat-developer/odo/pkg/api" + "github.com/redhat-developer/odo/pkg/testingutil/filesystem" +) + +func TestState_SetForwardedPorts(t *testing.T) { + + forwardedPort1 := api.ForwardedPort{ + ContainerName: "acontainer", + LocalAddress: "localhost", + LocalPort: 40001, + ContainerPort: 3000, + } + + type fields struct { + fs func() filesystem.Filesystem + getSecondsFromEpoch func() int64 + getpid func() int + } + type args struct { + fwPorts []api.ForwardedPort + } + tests := []struct { + name string + fields fields + args args + wantErr bool + checkState func(fs filesystem.Filesystem) error + }{ + // TODO: Add test cases. + { + name: "set forwarded ports", + fields: fields{ + fs: func() filesystem.Filesystem { + return filesystem.NewFakeFs() + }, + getSecondsFromEpoch: func() int64 { + return 13000 + }, + getpid: func() int { + return 100 + }, + }, + args: args{ + fwPorts: []api.ForwardedPort{forwardedPort1}, + }, + wantErr: false, + checkState: func(fs filesystem.Filesystem) error { + jsonContent, err := fs.ReadFile(_filepath) + if err != nil { + return err + } + var content Content + err = json.Unmarshal(jsonContent, &content) + if err != nil { + return err + } + if content.Timestamp != 13000 { + return fmt.Errorf("timestamp is %d, should be 13000", content.Timestamp) + } + if content.PID != 100 { + return fmt.Errorf("PID is %d, should be 100", content.PID) + } + expected := []api.ForwardedPort{forwardedPort1} + if !reflect.DeepEqual(content.ForwardedPorts, expected) { + return fmt.Errorf("Forwarded ports is %+v, should be %+v", content.ForwardedPorts, expected) + } + return nil + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fs := tt.fields.fs() + o := State{ + fs: fs, + getSecondsFromEpoch: tt.fields.getSecondsFromEpoch, + getpid: tt.fields.getpid, + } + if err := o.SetForwardedPorts(tt.args.fwPorts); (err != nil) != tt.wantErr { + t.Errorf("State.SetForwardedPorts() error = %v, wantErr %v", err, tt.wantErr) + } + if check := tt.checkState(fs); check != nil { + t.Error(check) + } + }) + } +} + +func TestState_SaveExit(t *testing.T) { + type fields struct { + fs func() filesystem.Filesystem + getSecondsFromEpoch func() int64 + getpid func() int + } + tests := []struct { + name string + fields fields + wantErr bool + checkState func(fs filesystem.Filesystem) error + }{ + { + name: "save exit", + fields: fields{ + fs: func() filesystem.Filesystem { + return filesystem.NewFakeFs() + }, + getSecondsFromEpoch: func() int64 { + return 13000 + }, + getpid: func() int { + return 100 + }, + }, + wantErr: false, + checkState: func(fs filesystem.Filesystem) error { + jsonContent, err := fs.ReadFile(_filepath) + if err != nil { + return err + } + var content Content + err = json.Unmarshal(jsonContent, &content) + if err != nil { + return err + } + if content.Timestamp != 13000 { + return fmt.Errorf("timestamp is %d, should be 13000", content.Timestamp) + } + if content.PID != 0 { + return fmt.Errorf("PID is %d, should be 0", content.PID) + } + if len(content.ForwardedPorts) != 0 { + return fmt.Errorf("Forwarded ports is %+v, should be empty", content.ForwardedPorts) + } + return nil + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fs := tt.fields.fs() + o := State{ + fs: fs, + getSecondsFromEpoch: tt.fields.getSecondsFromEpoch, + getpid: tt.fields.getpid, + } + if err := o.SaveExit(); (err != nil) != tt.wantErr { + t.Errorf("State.SaveExit() error = %v, wantErr %v", err, tt.wantErr) + } + if check := tt.checkState(fs); check != nil { + t.Error(check) + } + }) + } +} diff --git a/pkg/state/types.go b/pkg/state/types.go new file mode 100644 index 00000000000..67099716912 --- /dev/null +++ b/pkg/state/types.go @@ -0,0 +1,14 @@ +package state + +import ( + "github.com/redhat-developer/odo/pkg/api" +) + +type Content struct { + // Timestamp is the number of seconds from epoch at which the state were saved + Timestamp int64 `json:"timestamp"` + // PID is the pid of the running odo process, 0 if odo is not running + PID int `json:"pid"` + // ForwardedPorts are the ports forwarded during odo dev session + ForwardedPorts []api.ForwardedPort `json:"forwardedPorts"` +} diff --git a/pkg/watch/watch.go b/pkg/watch/watch.go index c40bd2b4c52..88c6aff7737 100644 --- a/pkg/watch/watch.go +++ b/pkg/watch/watch.go @@ -10,6 +10,7 @@ import ( "github.com/devfile/library/pkg/devfile/parser" _delete "github.com/redhat-developer/odo/pkg/component/delete" + "github.com/redhat-developer/odo/pkg/state" "github.com/fsnotify/fsnotify" gitignore "github.com/sabhiram/go-gitignore" @@ -32,11 +33,13 @@ const ( type WatchClient struct { deleteClient _delete.Client + stateClient state.Client } -func NewWatchClient(deleteClient _delete.Client) *WatchClient { +func NewWatchClient(deleteClient _delete.Client, stateClient state.Client) *WatchClient { return &WatchClient{ deleteClient: deleteClient, + stateClient: stateClient, } } @@ -373,7 +376,8 @@ func (o *WatchClient) Cleanup(devfileObj parser.DevfileObj, out io.Writer) error for _, fail := range failed { fmt.Fprintf(out, "Failed to delete the %q resource: %s\n", fail.GetKind(), fail.GetName()) } - return nil + + return o.stateClient.SaveExit() } func shouldIgnoreEvent(event fsnotify.Event) (ignoreEvent bool) { From 826e0c56fa4fb0ea7ea8f0c4c9467004366fbdfe Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Fri, 29 Apr 2022 13:51:27 +0200 Subject: [PATCH 02/12] Integration tests --- tests/integration/devfile/cmd_dev_test.go | 47 +++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/tests/integration/devfile/cmd_dev_test.go b/tests/integration/devfile/cmd_dev_test.go index 379927552ec..f66bed0acee 100644 --- a/tests/integration/devfile/cmd_dev_test.go +++ b/tests/integration/devfile/cmd_dev_test.go @@ -3,6 +3,7 @@ package devfile import ( "fmt" "io" + "io/ioutil" "net/http" "os" "path/filepath" @@ -1398,4 +1399,50 @@ var _ = Describe("odo dev command tests", func() { } }) }) + + When("a component with multiple endpoints is run", func() { + stateFile := ".odo/state.json" + var devSession helper.DevSession + BeforeEach(func() { + helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project-with-multiple-endpoints"), commonVar.Context) + helper.Cmd("odo", "project", "set", commonVar.Project).ShouldPass() + helper.Cmd("odo", "init", "--name", cmpName, "--devfile-path", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile-with-multiple-endpoints.yaml")).ShouldPass() + Expect(helper.VerifyFileExists(".odo/state.json")).To(BeFalse()) + var err error + devSession, _, _, _, err = helper.StartDevMode() + Expect(err).ToNot(HaveOccurred()) + }) + + AfterEach(func() { + // We stop the process so the process does not remain after the end of the tests + devSession.Kill() + devSession.WaitEnd() + }) + + It("should create a state file containing forwarded ports", func() { + Expect(helper.VerifyFileExists(stateFile)).To(BeTrue()) + contentJSON, err := ioutil.ReadFile(stateFile) + Expect(err).ToNot(HaveOccurred()) + helper.JsonPathContentIs(string(contentJSON), "forwardedPorts.0.containerName", "runtime") + helper.JsonPathContentIs(string(contentJSON), "forwardedPorts.1.containerName", "runtime") + helper.JsonPathContentIs(string(contentJSON), "forwardedPorts.0.localAddress", "127.0.0.1") + helper.JsonPathContentIs(string(contentJSON), "forwardedPorts.1.localAddress", "127.0.0.1") + helper.JsonPathContentIs(string(contentJSON), "forwardedPorts.0.containerPort", "3000") + helper.JsonPathContentIs(string(contentJSON), "forwardedPorts.1.containerPort", "4567") + }) + + When("odo dev is stopped", func() { + BeforeEach(func() { + devSession.Stop() + devSession.WaitEnd() + }) + + It("should remove forwarded ports from state file", func() { + Expect(helper.VerifyFileExists(stateFile)).To(BeTrue()) + contentJSON, err := ioutil.ReadFile(stateFile) + Expect(err).ToNot(HaveOccurred()) + helper.JsonPathContentIs(string(contentJSON), "forwardedPorts", "") + }) + }) + }) }) From dd741010106bcd7abb2f698be8477479fd4652c1 Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Fri, 29 Apr 2022 17:49:45 +0200 Subject: [PATCH 03/12] Documentation --- .../version-3.0.0/command-reference/dev.md | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/docs/website/versioned_docs/version-3.0.0/command-reference/dev.md b/docs/website/versioned_docs/version-3.0.0/command-reference/dev.md index 6df356c3a21..466a75af148 100644 --- a/docs/website/versioned_docs/version-3.0.0/command-reference/dev.md +++ b/docs/website/versioned_docs/version-3.0.0/command-reference/dev.md @@ -226,3 +226,25 @@ components: memoryLimit: 1024Mi mountSources: true ``` + +### State file + +When the command `odo dev` is executed, the state of the command isa saved in the file `./.odo/state.json`. + +This state file contains the PID of the `odo dev` running process (`0` if no command is running), the timestamp +at which the state has been last saved, and the currently forwarded ports. + +``` +{ + "timestamp": 1651247240, + "pid": 154474, + "forwardedPorts": [ + { + "containerName": "runtime", + "localAddress": "127.0.0.1", + "localPort": 40001, + "containerPort": 3000 + } + ] +} +``` From 05cdf9b8055f6dcbe2a28ab575205171516a3c6a Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Tue, 3 May 2022 08:15:57 +0200 Subject: [PATCH 04/12] Update docs/website/versioned_docs/version-3.0.0/command-reference/dev.md Co-authored-by: Armel Soro --- .../versioned_docs/version-3.0.0/command-reference/dev.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/website/versioned_docs/version-3.0.0/command-reference/dev.md b/docs/website/versioned_docs/version-3.0.0/command-reference/dev.md index 466a75af148..92f4e9c9db3 100644 --- a/docs/website/versioned_docs/version-3.0.0/command-reference/dev.md +++ b/docs/website/versioned_docs/version-3.0.0/command-reference/dev.md @@ -229,7 +229,7 @@ components: ### State file -When the command `odo dev` is executed, the state of the command isa saved in the file `./.odo/state.json`. +When the command `odo dev` is executed, the state of the command is saved in the file `./.odo/state.json`. This state file contains the PID of the `odo dev` running process (`0` if no command is running), the timestamp at which the state has been last saved, and the currently forwarded ports. From 808a0b10b798a04b2895be89d7ea9035873541d7 Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Tue, 3 May 2022 13:40:05 +0200 Subject: [PATCH 05/12] Fail when an error happens writing state file In case of error during initialization, odo dev cleanup the resources --- pkg/odo/cli/dev/dev.go | 34 +++++++++++++---------- pkg/watch/watch.go | 1 + tests/integration/devfile/cmd_dev_test.go | 16 +++++++++++ 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/pkg/odo/cli/dev/dev.go b/pkg/odo/cli/dev/dev.go index e4465c1ff69..e55726b1334 100644 --- a/pkg/odo/cli/dev/dev.go +++ b/pkg/odo/cli/dev/dev.go @@ -178,14 +178,22 @@ func (o *DevOptions) Validate() error { return nil } -func (o *DevOptions) Run(ctx context.Context) error { - var err error - var platformContext = kubernetes.KubernetesContext{ - Namespace: o.Context.GetProject(), - } - var path = filepath.Dir(o.Context.EnvSpecificInfo.GetDevfilePath()) - devfileName := o.EnvSpecificInfo.GetDevfileObj().GetMetadataName() - namespace := o.GetProject() +func (o *DevOptions) Run(ctx context.Context) (err error) { + var ( + devFileObj = o.Context.EnvSpecificInfo.GetDevfileObj() + platformContext = kubernetes.KubernetesContext{ + Namespace: o.Context.GetProject(), + } + path = filepath.Dir(o.Context.EnvSpecificInfo.GetDevfilePath()) + devfileName = devFileObj.GetMetadataName() + namespace = o.GetProject() + ) + + defer func() { + if err != nil { + _ = o.clientset.WatchClient.Cleanup(devFileObj, log.GetStdout()) + } + }() // Output what the command is doing / information log.Title("Developing using the "+devfileName+" Devfile", @@ -193,13 +201,13 @@ func (o *DevOptions) Run(ctx context.Context) error { "odo version: "+version.VERSION) log.Section("Deploying to the cluster in developer mode") - err = o.clientset.DevClient.Start(o.Context.EnvSpecificInfo.GetDevfileObj(), platformContext, o.ignorePaths, path, o.debugFlag) + err = o.clientset.DevClient.Start(devFileObj, platformContext, o.ignorePaths, path, o.debugFlag) if err != nil { return err } // get the endpoint/port information for containers in devfile and setup port-forwarding - containers, err := o.Context.EnvSpecificInfo.GetDevfileObj().Data.GetComponents(parsercommon.DevfileOptions{ + containers, err := devFileObj.Data.GetComponents(parsercommon.DevfileOptions{ ComponentOptions: parsercommon.ComponentOptions{ComponentType: v1alpha2.ContainerComponentType}, }) if err != nil { @@ -216,7 +224,7 @@ func (o *DevOptions) Run(ctx context.Context) error { for _, v1 := range portPairs { portPairsSlice = append(portPairsSlice, v1...) } - pod, err := o.clientset.KubernetesClient.GetPodUsingComponentName(o.Context.EnvSpecificInfo.GetDevfileObj().GetMetadataName()) + pod, err := o.clientset.KubernetesClient.GetPodUsingComponentName(devFileObj.GetMetadataName()) if err != nil { return err } @@ -235,11 +243,9 @@ func (o *DevOptions) Run(ctx context.Context) error { portsBuf.Wait() err = o.clientset.StateClient.SetForwardedPorts(portsBuf.GetForwaredPorts()) if err != nil { - fmt.Printf("unable to save forwarded ports to state file: %v", err) + return fmt.Errorf("unable to save forwarded ports to state file: %v", err) } - devFileObj := o.Context.EnvSpecificInfo.GetDevfileObj() - scontext.SetComponentType(ctx, component.GetComponentTypeFromDevfileMetadata(devFileObj.Data.GetMetadata())) scontext.SetLanguage(ctx, devFileObj.Data.GetMetadata().Language) scontext.SetProjectType(ctx, devFileObj.Data.GetMetadata().ProjectType) diff --git a/pkg/watch/watch.go b/pkg/watch/watch.go index 88c6aff7737..724827bcde2 100644 --- a/pkg/watch/watch.go +++ b/pkg/watch/watch.go @@ -359,6 +359,7 @@ func processEvents(changedFiles, deletedPaths []string, parameters WatchParamete } func (o *WatchClient) Cleanup(devfileObj parser.DevfileObj, out io.Writer) error { + fmt.Fprintln(out, "Cleaning resources, please wait") isInnerLoopDeployed, resources, err := o.deleteClient.ListResourcesToDeleteFromDevfile(devfileObj, "app") if err != nil { fmt.Fprintf(out, "failed to delete inner loop resources: %v", err) diff --git a/tests/integration/devfile/cmd_dev_test.go b/tests/integration/devfile/cmd_dev_test.go index f66bed0acee..c73334ba9a2 100644 --- a/tests/integration/devfile/cmd_dev_test.go +++ b/tests/integration/devfile/cmd_dev_test.go @@ -167,6 +167,22 @@ var _ = Describe("odo dev command tests", func() { Expect(err).ToNot(HaveOccurred()) }) + When("a state file is not writable", func() { + BeforeEach(func() { + stateFile := filepath.Join(commonVar.Context, ".odo", "state.json") + helper.MakeDir(filepath.Dir(stateFile)) + Expect(helper.CreateFileWithContent(stateFile, "")).ToNot(HaveOccurred()) + Expect(os.Chmod(stateFile, 0400)).ToNot(HaveOccurred()) + }) + It("should fail running odo dev", func() { + res := helper.Cmd("odo", "dev", "--random-ports").ShouldFail() + stdout := res.Out() + stderr := res.Err() + Expect(stdout).To(ContainSubstring("Cleaning")) + Expect(stderr).To(ContainSubstring("unable to save forwarded ports to state file")) + }) + }) + When("odo dev is executed", func() { BeforeEach(func() { From 999ad9167c51ed5d53fa6965b0abad1059815286 Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Tue, 3 May 2022 13:42:54 +0200 Subject: [PATCH 06/12] Fix typo --- pkg/odo/cli/dev/dev.go | 2 +- pkg/odo/cli/dev/writer.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/odo/cli/dev/dev.go b/pkg/odo/cli/dev/dev.go index e55726b1334..4c8f148919d 100644 --- a/pkg/odo/cli/dev/dev.go +++ b/pkg/odo/cli/dev/dev.go @@ -241,7 +241,7 @@ func (o *DevOptions) Run(ctx context.Context) (err error) { }() portsBuf.Wait() - err = o.clientset.StateClient.SetForwardedPorts(portsBuf.GetForwaredPorts()) + err = o.clientset.StateClient.SetForwardedPorts(portsBuf.GetForwardedPorts()) if err != nil { return fmt.Errorf("unable to save forwarded ports to state file: %v", err) } diff --git a/pkg/odo/cli/dev/writer.go b/pkg/odo/cli/dev/writer.go index f93e6d73077..4d09236426f 100644 --- a/pkg/odo/cli/dev/writer.go +++ b/pkg/odo/cli/dev/writer.go @@ -65,7 +65,7 @@ func (o *PortWriter) Wait() { <-o.end } -func (o *PortWriter) GetForwaredPorts() []api.ForwardedPort { +func (o *PortWriter) GetForwardedPorts() []api.ForwardedPort { return o.fwPorts } From 110d684edb7f6198185e568beab67c8832b0a41e Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Tue, 3 May 2022 13:47:52 +0200 Subject: [PATCH 07/12] Test localPort matches a number --- tests/helper/helper_generic.go | 6 ++++++ tests/integration/devfile/cmd_dev_test.go | 2 ++ 2 files changed, 8 insertions(+) diff --git a/tests/helper/helper_generic.go b/tests/helper/helper_generic.go index b458b5bc3df..3581e26720d 100644 --- a/tests/helper/helper_generic.go +++ b/tests/helper/helper_generic.go @@ -271,6 +271,12 @@ func JsonPathContentContain(json string, path string, value string) bool { return true } +func JsonPathContentMatch(json string, path string, regexp string) bool { + result := gjson.Get(json, path) + Expect(result.String()).To(MatchRegexp(regexp), fmt.Sprintf("content of path %q should match %q but is %q", path, regexp, result.String())) + return true +} + // SetProjectName sets projectNames based on the neame of the test file name (withouth path and replacing _ with -), line number of current ginkgo execution, and a random string of 3 letters func SetProjectName() string { // Get current test filename and remove file path, file extension and replace undescores with hyphens diff --git a/tests/integration/devfile/cmd_dev_test.go b/tests/integration/devfile/cmd_dev_test.go index c73334ba9a2..9b2b9058038 100644 --- a/tests/integration/devfile/cmd_dev_test.go +++ b/tests/integration/devfile/cmd_dev_test.go @@ -1445,6 +1445,8 @@ var _ = Describe("odo dev command tests", func() { helper.JsonPathContentIs(string(contentJSON), "forwardedPorts.1.localAddress", "127.0.0.1") helper.JsonPathContentIs(string(contentJSON), "forwardedPorts.0.containerPort", "3000") helper.JsonPathContentIs(string(contentJSON), "forwardedPorts.1.containerPort", "4567") + helper.JsonPathContentMatch(string(contentJSON), "forwardedPorts.0.localPort", "[0-9]+") + helper.JsonPathContentMatch(string(contentJSON), "forwardedPorts.1.localPort", "[0-9]+") }) When("odo dev is stopped", func() { From 3aef02ef5855a7820ac9c3fe3d55511e5b06d42d Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Tue, 3 May 2022 15:43:11 +0200 Subject: [PATCH 08/12] Remove PID and timestamp from state + rename to devstate.json --- pkg/state/const.go | 2 +- pkg/state/state.go | 25 +++-------------------- pkg/state/state_test.go | 20 ++---------------- pkg/state/types.go | 4 ---- tests/integration/devfile/cmd_dev_test.go | 8 ++++---- 5 files changed, 10 insertions(+), 49 deletions(-) diff --git a/pkg/state/const.go b/pkg/state/const.go index 225e5fbc693..f81a14eaf4f 100644 --- a/pkg/state/const.go +++ b/pkg/state/const.go @@ -1,3 +1,3 @@ package state -const _filepath = "./.odo/state.json" +const _filepath = "./.odo/devstate.json" diff --git a/pkg/state/state.go b/pkg/state/state.go index 883008b394d..e291660e55f 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -2,48 +2,33 @@ package state import ( "encoding/json" - "os" - "time" "github.com/redhat-developer/odo/pkg/api" "github.com/redhat-developer/odo/pkg/testingutil/filesystem" ) type State struct { - content Content - fs filesystem.Filesystem - getSecondsFromEpoch func() int64 - getpid func() int + content Content + fs filesystem.Filesystem } func NewStateClient(fs filesystem.Filesystem) *State { return &State{ - fs: fs, - getSecondsFromEpoch: getSecondsFromEpoch, - getpid: os.Getpid, + fs: fs, } } func (o *State) SetForwardedPorts(fwPorts []api.ForwardedPort) error { // TODO(feloy) When other data is persisted into the state file, it will be needed to read the file first o.content.ForwardedPorts = fwPorts - o.setMetadata() return o.save() } func (o *State) SaveExit() error { o.content.ForwardedPorts = nil - o.content.PID = 0 - o.content.Timestamp = o.getSecondsFromEpoch() return o.save() } -// setMetadata sets the metadata in the state with current PID and epoch -func (o *State) setMetadata() { - o.content.PID = o.getpid() - o.content.Timestamp = o.getSecondsFromEpoch() -} - // save writes the content structure in json format in file func (o *State) save() error { jsonContent, err := json.MarshalIndent(o.content, "", " ") @@ -53,7 +38,3 @@ func (o *State) save() error { // .odo directory is supposed to exist, don't create it return o.fs.WriteFile(_filepath, jsonContent, 0644) } - -func getSecondsFromEpoch() int64 { - return time.Now().Unix() -} diff --git a/pkg/state/state_test.go b/pkg/state/state_test.go index 0e9bdb46303..f629743d028 100644 --- a/pkg/state/state_test.go +++ b/pkg/state/state_test.go @@ -62,12 +62,6 @@ func TestState_SetForwardedPorts(t *testing.T) { if err != nil { return err } - if content.Timestamp != 13000 { - return fmt.Errorf("timestamp is %d, should be 13000", content.Timestamp) - } - if content.PID != 100 { - return fmt.Errorf("PID is %d, should be 100", content.PID) - } expected := []api.ForwardedPort{forwardedPort1} if !reflect.DeepEqual(content.ForwardedPorts, expected) { return fmt.Errorf("Forwarded ports is %+v, should be %+v", content.ForwardedPorts, expected) @@ -80,9 +74,7 @@ func TestState_SetForwardedPorts(t *testing.T) { t.Run(tt.name, func(t *testing.T) { fs := tt.fields.fs() o := State{ - fs: fs, - getSecondsFromEpoch: tt.fields.getSecondsFromEpoch, - getpid: tt.fields.getpid, + fs: fs, } if err := o.SetForwardedPorts(tt.args.fwPorts); (err != nil) != tt.wantErr { t.Errorf("State.SetForwardedPorts() error = %v, wantErr %v", err, tt.wantErr) @@ -130,12 +122,6 @@ func TestState_SaveExit(t *testing.T) { if err != nil { return err } - if content.Timestamp != 13000 { - return fmt.Errorf("timestamp is %d, should be 13000", content.Timestamp) - } - if content.PID != 0 { - return fmt.Errorf("PID is %d, should be 0", content.PID) - } if len(content.ForwardedPorts) != 0 { return fmt.Errorf("Forwarded ports is %+v, should be empty", content.ForwardedPorts) } @@ -147,9 +133,7 @@ func TestState_SaveExit(t *testing.T) { t.Run(tt.name, func(t *testing.T) { fs := tt.fields.fs() o := State{ - fs: fs, - getSecondsFromEpoch: tt.fields.getSecondsFromEpoch, - getpid: tt.fields.getpid, + fs: fs, } if err := o.SaveExit(); (err != nil) != tt.wantErr { t.Errorf("State.SaveExit() error = %v, wantErr %v", err, tt.wantErr) diff --git a/pkg/state/types.go b/pkg/state/types.go index 67099716912..1a68bebe629 100644 --- a/pkg/state/types.go +++ b/pkg/state/types.go @@ -5,10 +5,6 @@ import ( ) type Content struct { - // Timestamp is the number of seconds from epoch at which the state were saved - Timestamp int64 `json:"timestamp"` - // PID is the pid of the running odo process, 0 if odo is not running - PID int `json:"pid"` // ForwardedPorts are the ports forwarded during odo dev session ForwardedPorts []api.ForwardedPort `json:"forwardedPorts"` } diff --git a/tests/integration/devfile/cmd_dev_test.go b/tests/integration/devfile/cmd_dev_test.go index 9b2b9058038..f63a829b031 100644 --- a/tests/integration/devfile/cmd_dev_test.go +++ b/tests/integration/devfile/cmd_dev_test.go @@ -169,10 +169,10 @@ var _ = Describe("odo dev command tests", func() { When("a state file is not writable", func() { BeforeEach(func() { - stateFile := filepath.Join(commonVar.Context, ".odo", "state.json") + stateFile := filepath.Join(commonVar.Context, ".odo", "devstate.json") helper.MakeDir(filepath.Dir(stateFile)) Expect(helper.CreateFileWithContent(stateFile, "")).ToNot(HaveOccurred()) - Expect(os.Chmod(stateFile, 0400)).ToNot(HaveOccurred()) + Expect(os.Chmod(stateFile, 0000)).ToNot(HaveOccurred()) }) It("should fail running odo dev", func() { res := helper.Cmd("odo", "dev", "--random-ports").ShouldFail() @@ -1417,13 +1417,13 @@ var _ = Describe("odo dev command tests", func() { }) When("a component with multiple endpoints is run", func() { - stateFile := ".odo/state.json" + stateFile := ".odo/devstate.json" var devSession helper.DevSession BeforeEach(func() { helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project-with-multiple-endpoints"), commonVar.Context) helper.Cmd("odo", "project", "set", commonVar.Project).ShouldPass() helper.Cmd("odo", "init", "--name", cmpName, "--devfile-path", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile-with-multiple-endpoints.yaml")).ShouldPass() - Expect(helper.VerifyFileExists(".odo/state.json")).To(BeFalse()) + Expect(helper.VerifyFileExists(".odo/devstate.json")).To(BeFalse()) var err error devSession, _, _, _, err = helper.StartDevMode() Expect(err).ToNot(HaveOccurred()) From 10586d90693c6edaecef4f5c2e2474ec164fb41e Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Tue, 3 May 2022 16:08:10 +0200 Subject: [PATCH 09/12] Run IBM cloud tests as non root --- .ibm/images/Dockerfile | 13 ++++++++++--- tests/integration/devfile/cmd_dev_test.go | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.ibm/images/Dockerfile b/.ibm/images/Dockerfile index 9e51ed93897..26d4d768abb 100644 --- a/.ibm/images/Dockerfile +++ b/.ibm/images/Dockerfile @@ -1,8 +1,6 @@ FROM golang:1.17 RUN curl -fsSL https://clis.cloud.ibm.com/install/linux | sh && \ - ibmcloud plugin install -f cloud-object-storage && \ - ibmcloud plugin install -f kubernetes-service && \ curl -sLO https://github.com/cli/cli/releases/download/v2.1.0/gh_2.1.0_linux_amd64.deb && \ apt install ./gh_2.1.0_linux_amd64.deb && \ curl -sLO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl && \ @@ -15,4 +13,13 @@ RUN curl -fsSL https://clis.cloud.ibm.com/install/linux | sh && \ apt-get install -y sshpass && \ rm -rf /var/lib/apt/lists/* -RUN go get github.com/kadel/odo-robot@965ea0dd848856691bfc76e6824a8b787b950045 +# Create non-root user and associated home directory +RUN useradd -u 2001 tester +RUN mkdir /home/tester +RUN chown tester:tester /home/tester +# Change to non-root privilege +USER tester + +RUN go get github.com/kadel/odo-robot@965ea0dd848856691bfc76e6824a8b787b950045 && \ + ibmcloud plugin install -f cloud-object-storage && \ + ibmcloud plugin install -f kubernetes-service diff --git a/tests/integration/devfile/cmd_dev_test.go b/tests/integration/devfile/cmd_dev_test.go index f63a829b031..5373cc5776e 100644 --- a/tests/integration/devfile/cmd_dev_test.go +++ b/tests/integration/devfile/cmd_dev_test.go @@ -172,7 +172,7 @@ var _ = Describe("odo dev command tests", func() { stateFile := filepath.Join(commonVar.Context, ".odo", "devstate.json") helper.MakeDir(filepath.Dir(stateFile)) Expect(helper.CreateFileWithContent(stateFile, "")).ToNot(HaveOccurred()) - Expect(os.Chmod(stateFile, 0000)).ToNot(HaveOccurred()) + Expect(os.Chmod(stateFile, 0400)).ToNot(HaveOccurred()) }) It("should fail running odo dev", func() { res := helper.Cmd("odo", "dev", "--random-ports").ShouldFail() From 1f436b3c54034090da66258a24c16cda0780f471 Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Tue, 3 May 2022 16:35:37 +0200 Subject: [PATCH 10/12] Fix doc --- .../versioned_docs/version-3.0.0/command-reference/dev.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/website/versioned_docs/version-3.0.0/command-reference/dev.md b/docs/website/versioned_docs/version-3.0.0/command-reference/dev.md index 92f4e9c9db3..c7f0b164616 100644 --- a/docs/website/versioned_docs/version-3.0.0/command-reference/dev.md +++ b/docs/website/versioned_docs/version-3.0.0/command-reference/dev.md @@ -236,8 +236,6 @@ at which the state has been last saved, and the currently forwarded ports. ``` { - "timestamp": 1651247240, - "pid": 154474, "forwardedPorts": [ { "containerName": "runtime", From 64ea46167d18e05a85d2dd087d5fd0f70c65be13 Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Tue, 3 May 2022 19:41:13 +0200 Subject: [PATCH 11/12] Review --- .ibm/images/Dockerfile | 4 +--- .../version-3.0.0/command-reference/dev.md | 2 +- tests/helper/helper_generic.go | 16 +++++++++------- tests/integration/devfile/cmd_alizer_test.go | 6 +++--- tests/integration/devfile/cmd_dev_test.go | 4 ++-- .../integration/devfile/cmd_devfile_init_test.go | 16 ++++++++-------- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/.ibm/images/Dockerfile b/.ibm/images/Dockerfile index 26d4d768abb..817cd63e628 100644 --- a/.ibm/images/Dockerfile +++ b/.ibm/images/Dockerfile @@ -14,9 +14,7 @@ RUN curl -fsSL https://clis.cloud.ibm.com/install/linux | sh && \ rm -rf /var/lib/apt/lists/* # Create non-root user and associated home directory -RUN useradd -u 2001 tester -RUN mkdir /home/tester -RUN chown tester:tester /home/tester +RUN useradd -u 2001 --create-home tester # Change to non-root privilege USER tester diff --git a/docs/website/versioned_docs/version-3.0.0/command-reference/dev.md b/docs/website/versioned_docs/version-3.0.0/command-reference/dev.md index c7f0b164616..b075a75bdc3 100644 --- a/docs/website/versioned_docs/version-3.0.0/command-reference/dev.md +++ b/docs/website/versioned_docs/version-3.0.0/command-reference/dev.md @@ -229,7 +229,7 @@ components: ### State file -When the command `odo dev` is executed, the state of the command is saved in the file `./.odo/state.json`. +When the command `odo dev` is executed, the state of the command is saved in the file `./.odo/devstate.json`. This state file contains the PID of the `odo dev` running process (`0` if no command is running), the timestamp at which the state has been last saved, and the currently forwarded ports. diff --git a/tests/helper/helper_generic.go b/tests/helper/helper_generic.go index 3581e26720d..d0ce641490e 100644 --- a/tests/helper/helper_generic.go +++ b/tests/helper/helper_generic.go @@ -258,23 +258,25 @@ func CommonAfterEach(commonVar CommonVar) { } // JsonPathContentIs expects that the content of the path to equal value -func JsonPathContentIs(json string, path string, value string) bool { +func JsonPathContentIs(json string, path string, value string) { result := gjson.Get(json, path) Expect(result.String()).To(Equal(value), fmt.Sprintf("content of path %q should be %q but is %q", path, value, result.String())) - return true } // JsonPathContentContain expects that the content of the path to contain value -func JsonPathContentContain(json string, path string, value string) bool { +func JsonPathContentContain(json string, path string, value string) { result := gjson.Get(json, path) Expect(result.String()).To(ContainSubstring(value), fmt.Sprintf("content of path %q should contain %q but is %q", path, value, result.String())) - return true } -func JsonPathContentMatch(json string, path string, regexp string) bool { +func JsonPathContentIsValidUserPort(json string, path string) { result := gjson.Get(json, path) - Expect(result.String()).To(MatchRegexp(regexp), fmt.Sprintf("content of path %q should match %q but is %q", path, regexp, result.String())) - return true + intVal, err := strconv.Atoi(result.String()) + Expect(err).ToNot(HaveOccurred()) + Expect(intVal).To(SatisfyAll( + BeNumerically(">=", 1024), + BeNumerically("<=", 65535), + )) } // SetProjectName sets projectNames based on the neame of the test file name (withouth path and replacing _ with -), line number of current ginkgo execution, and a random string of 3 letters diff --git a/tests/integration/devfile/cmd_alizer_test.go b/tests/integration/devfile/cmd_alizer_test.go index 7dde22a9db8..cc83ecbda4d 100644 --- a/tests/integration/devfile/cmd_alizer_test.go +++ b/tests/integration/devfile/cmd_alizer_test.go @@ -33,8 +33,8 @@ var _ = Describe("odo alizer command tests", func() { stdout, stderr := res.Out(), res.Err() Expect(stderr).To(BeEmpty()) Expect(helper.IsJSON(stdout)).To(BeTrue()) - Expect(helper.JsonPathContentIs(stdout, "devfile", "nodejs")) - Expect(helper.JsonPathContentIs(stdout, "devfileRegistry", "DefaultDevfileRegistry")) + helper.JsonPathContentIs(stdout, "devfile", "nodejs") + helper.JsonPathContentIs(stdout, "devfileRegistry", "DefaultDevfileRegistry") }) }) @@ -43,7 +43,7 @@ var _ = Describe("odo alizer command tests", func() { stdout, stderr := res.Out(), res.Err() Expect(stdout).To(BeEmpty()) Expect(helper.IsJSON(stderr)).To(BeTrue()) - Expect(helper.JsonPathContentContain(stderr, "message", "No valid devfile found for project in")) + helper.JsonPathContentContain(stderr, "message", "No valid devfile found for project in") }) It("alizer should fail without json output", func() { diff --git a/tests/integration/devfile/cmd_dev_test.go b/tests/integration/devfile/cmd_dev_test.go index 5373cc5776e..177b04f4e33 100644 --- a/tests/integration/devfile/cmd_dev_test.go +++ b/tests/integration/devfile/cmd_dev_test.go @@ -1445,8 +1445,8 @@ var _ = Describe("odo dev command tests", func() { helper.JsonPathContentIs(string(contentJSON), "forwardedPorts.1.localAddress", "127.0.0.1") helper.JsonPathContentIs(string(contentJSON), "forwardedPorts.0.containerPort", "3000") helper.JsonPathContentIs(string(contentJSON), "forwardedPorts.1.containerPort", "4567") - helper.JsonPathContentMatch(string(contentJSON), "forwardedPorts.0.localPort", "[0-9]+") - helper.JsonPathContentMatch(string(contentJSON), "forwardedPorts.1.localPort", "[0-9]+") + helper.JsonPathContentIsValidUserPort(string(contentJSON), "forwardedPorts.0.localPort") + helper.JsonPathContentIsValidUserPort(string(contentJSON), "forwardedPorts.1.localPort") }) When("odo dev is stopped", func() { diff --git a/tests/integration/devfile/cmd_devfile_init_test.go b/tests/integration/devfile/cmd_devfile_init_test.go index 482ac1e655e..be9fbed64f1 100644 --- a/tests/integration/devfile/cmd_devfile_init_test.go +++ b/tests/integration/devfile/cmd_devfile_init_test.go @@ -40,7 +40,7 @@ var _ = Describe("odo devfile init command tests", func() { stdout, stderr := res.Out(), res.Err() Expect(stdout).To(BeEmpty()) Expect(helper.IsJSON(stderr)).To(BeTrue()) - Expect(helper.JsonPathContentIs(stderr, "message", "parameters are expected to select a devfile")) + helper.JsonPathContentIs(stderr, "message", "parameters are expected to select a devfile") }) By("running odo init with incomplete flags and JSON output", func() { @@ -48,7 +48,7 @@ var _ = Describe("odo devfile init command tests", func() { stdout, stderr := res.Out(), res.Err() Expect(stdout).To(BeEmpty()) Expect(helper.IsJSON(stderr)).To(BeTrue()) - Expect(helper.JsonPathContentContain(stderr, "message", "either --devfile or --devfile-path parameter should be specified")) + helper.JsonPathContentContain(stderr, "message", "either --devfile or --devfile-path parameter should be specified") }) By("keeping an empty directory when running odo init with wrong starter name", func() { @@ -124,12 +124,12 @@ var _ = Describe("odo devfile init command tests", func() { stdout, stderr := res.Out(), res.Err() Expect(stderr).To(BeEmpty()) Expect(helper.IsJSON(stdout)).To(BeTrue()) - Expect(helper.JsonPathContentIs(stdout, "devfilePath", filepath.Join(commonVar.Context, "devfile.yaml"))) - Expect(helper.JsonPathContentIs(stdout, "devfileData.devfile.schemaVersion", "2.0.0")) - Expect(helper.JsonPathContentIs(stdout, "devfileData.supportedOdoFeatures.dev", "true")) - Expect(helper.JsonPathContentIs(stdout, "devfileData.supportedOdoFeatures.debug", "false")) - Expect(helper.JsonPathContentIs(stdout, "devfileData.supportedOdoFeatures.deploy", "false")) - Expect(helper.JsonPathContentIs(stdout, "managedBy", "odo")) + helper.JsonPathContentIs(stdout, "devfilePath", filepath.Join(commonVar.Context, "devfile.yaml")) + helper.JsonPathContentIs(stdout, "devfileData.devfile.schemaVersion", "2.0.0") + helper.JsonPathContentIs(stdout, "devfileData.supportedOdoFeatures.dev", "true") + helper.JsonPathContentIs(stdout, "devfileData.supportedOdoFeatures.debug", "false") + helper.JsonPathContentIs(stdout, "devfileData.supportedOdoFeatures.deploy", "false") + helper.JsonPathContentIs(stdout, "managedBy", "odo") }) }) When("using --devfile-path flag with a local devfile", func() { From 6382aba4434d7be2da63b9767130045861a42839 Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Wed, 4 May 2022 10:02:21 +0200 Subject: [PATCH 12/12] Remove reference to PID/timestamp from doc --- .../versioned_docs/version-3.0.0/command-reference/dev.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/website/versioned_docs/version-3.0.0/command-reference/dev.md b/docs/website/versioned_docs/version-3.0.0/command-reference/dev.md index b075a75bdc3..3cf323b429a 100644 --- a/docs/website/versioned_docs/version-3.0.0/command-reference/dev.md +++ b/docs/website/versioned_docs/version-3.0.0/command-reference/dev.md @@ -231,8 +231,7 @@ components: When the command `odo dev` is executed, the state of the command is saved in the file `./.odo/devstate.json`. -This state file contains the PID of the `odo dev` running process (`0` if no command is running), the timestamp -at which the state has been last saved, and the currently forwarded ports. +This state file contains the currently forwarded ports. ``` {