Skip to content

Commit

Permalink
Adds debug command for devfiles
Browse files Browse the repository at this point in the history
  • Loading branch information
mik-dass committed Apr 30, 2020
1 parent bd48c52 commit e362c28
Show file tree
Hide file tree
Showing 23 changed files with 788 additions and 99 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ jobs:
- travis_wait make test-cmd-url
- travis_wait make test-cmd-devfile-url
- travis_wait make test-cmd-debug
- travis_wait make test-cmd-devfile-debug
- odo logout

# Run service-catalog e2e tests
Expand Down
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,11 @@ test-cmd-url:
test-cmd-devfile-url:
ginkgo $(GINKGO_FLAGS) -focus="odo devfile url command tests" tests/integration/devfile/

# Run odo debug devfile command tests
.PHONY: test-cmd-devfile-debug
test-cmd-devfile-debug:
ginkgo $(GINKGO_FLAGS) -focus="odo devfile debug command tests" tests/integration/devfile/

# Run odo push docker devfile command tests
.PHONY: test-cmd-docker-devfile-push
test-cmd-docker-devfile-push:
Expand Down
20 changes: 12 additions & 8 deletions pkg/debug/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"encoding/json"
"errors"
"github.com/golang/glog"
"github.com/openshift/odo/pkg/occlient"
"github.com/openshift/odo/pkg/testingutil/filesystem"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"net"
Expand All @@ -23,18 +22,23 @@ type OdoDebugFile struct {
}

type OdoDebugFileSpec struct {
App string `json:"app"`
App string `json:"app,omitempty"`
DebugProcessID int `json:"debugProcessID"`
RemotePort int `json:"remotePort"`
LocalPort int `json:"localPort"`
}

// GetDebugInfoFilePath gets the file path of the debug info file
func GetDebugInfoFilePath(client *occlient.Client, componentName, appName string) string {
func GetDebugInfoFilePath(componentName, appName string, projectName string) string {
tempDir := os.TempDir()
debugFileSuffix := "odo-debug.json"
s := []string{client.Namespace, appName, componentName, debugFileSuffix}
debugFileName := strings.Join(s, "-")
var arr []string
if appName == "" {
arr = []string{projectName, componentName, debugFileSuffix}
} else {
arr = []string{projectName, appName, componentName, debugFileSuffix}
}
debugFileName := strings.Join(arr, "-")
return filepath.Join(tempDir, debugFileName)
}

Expand Down Expand Up @@ -65,7 +69,7 @@ func createDebugInfoFile(f *DefaultPortForwarder, portPair string, fs filesystem
},
ObjectMeta: metav1.ObjectMeta{
Name: f.componentName,
Namespace: f.client.Namespace,
Namespace: f.projectName,
},
Spec: OdoDebugFileSpec{
App: f.appName,
Expand All @@ -80,7 +84,7 @@ func createDebugInfoFile(f *DefaultPortForwarder, portPair string, fs filesystem
}

// writes the data to the debug info file
file, err := fs.OpenFile(GetDebugInfoFilePath(f.client, f.componentName, f.appName), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
file, err := fs.OpenFile(GetDebugInfoFilePath(f.componentName, f.appName, f.projectName), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
return err
}
Expand All @@ -101,7 +105,7 @@ func GetDebugInfo(f *DefaultPortForwarder) (OdoDebugFile, bool) {
// returns true if debugging is running else false
func getDebugInfo(f *DefaultPortForwarder, fs filesystem.Filesystem) (OdoDebugFile, bool) {
// gets the debug info file path and reads/unmarshals it
debugInfoFilePath := GetDebugInfoFilePath(f.client, f.componentName, f.appName)
debugInfoFilePath := GetDebugInfoFilePath(f.componentName, f.appName, f.projectName)
readFile, err := fs.ReadFile(debugInfoFilePath)
if err != nil {
glog.V(4).Infof("the debug %v is not present", debugInfoFilePath)
Expand Down
21 changes: 8 additions & 13 deletions pkg/debug/info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"reflect"
"testing"

"github.com/openshift/odo/pkg/occlient"
"github.com/openshift/odo/pkg/testingutil"
"github.com/openshift/odo/pkg/testingutil/filesystem"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -59,6 +58,7 @@ func Test_createDebugInfoFile(t *testing.T) {
defaultPortForwarder: &DefaultPortForwarder{
componentName: "nodejs-ex",
appName: "app",
projectName: "testing-1",
},
portPair: "5858:9001",
fs: fs,
Expand Down Expand Up @@ -88,6 +88,7 @@ func Test_createDebugInfoFile(t *testing.T) {
defaultPortForwarder: &DefaultPortForwarder{
componentName: "nodejs-ex",
appName: "app",
projectName: "testing-1",
},
portPair: "5758:9004",
fs: fs,
Expand Down Expand Up @@ -115,12 +116,7 @@ func Test_createDebugInfoFile(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

// Fake the client with the appropriate arguments
client, _ := occlient.FakeNew()
client.Namespace = "testing-1"
tt.args.defaultPortForwarder.client = client

debugFilePath := GetDebugInfoFilePath(client, tt.args.defaultPortForwarder.componentName, tt.args.defaultPortForwarder.appName)
debugFilePath := GetDebugInfoFilePath(tt.args.defaultPortForwarder.componentName, tt.args.defaultPortForwarder.appName, tt.args.defaultPortForwarder.projectName)
// create a already existing file
if tt.alreadyExistFile {
_, err := testingutil.MkFileWithContent(debugFilePath, "blah", fs)
Expand Down Expand Up @@ -177,6 +173,7 @@ func Test_getDebugInfo(t *testing.T) {
defaultPortForwarder: &DefaultPortForwarder{
appName: "app",
componentName: "nodejs-ex",
projectName: "testing-1",
},
fs: fs,
},
Expand Down Expand Up @@ -222,6 +219,7 @@ func Test_getDebugInfo(t *testing.T) {
defaultPortForwarder: &DefaultPortForwarder{
appName: "app",
componentName: "nodejs-ex",
projectName: "testing-1",
},
fs: fs,
},
Expand All @@ -237,6 +235,7 @@ func Test_getDebugInfo(t *testing.T) {
defaultPortForwarder: &DefaultPortForwarder{
appName: "app",
componentName: "nodejs-ex",
projectName: "testing-1",
},
fs: fs,
},
Expand Down Expand Up @@ -267,6 +266,7 @@ func Test_getDebugInfo(t *testing.T) {
defaultPortForwarder: &DefaultPortForwarder{
appName: "app",
componentName: "nodejs-ex",
projectName: "testing-1",
},
fs: fs,
},
Expand Down Expand Up @@ -295,11 +295,6 @@ func Test_getDebugInfo(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

// Fake the client with the appropriate arguments
client, _ := occlient.FakeNew()
client.Namespace = "testing-1"
tt.args.defaultPortForwarder.client = client

freePort, err := util.HttpGetFreePort()
if err != nil {
t.Errorf("error occured while getting a free port, cause: %v", err)
Expand All @@ -313,7 +308,7 @@ func Test_getDebugInfo(t *testing.T) {
tt.wantDebugFile.Spec.LocalPort = freePort
}

odoDebugFilePath := GetDebugInfoFilePath(tt.args.defaultPortForwarder.client, tt.args.defaultPortForwarder.componentName, tt.args.defaultPortForwarder.appName)
odoDebugFilePath := GetDebugInfoFilePath(tt.args.defaultPortForwarder.componentName, tt.args.defaultPortForwarder.appName, tt.args.defaultPortForwarder.projectName)
if tt.fileExists {
fakeString, err := fakeOdoDebugFileString(tt.readDebugFile.TypeMeta,
tt.readDebugFile.Spec.DebugProcessID,
Expand Down
66 changes: 39 additions & 27 deletions pkg/debug/portforward.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
package debug

import (
"github.com/openshift/odo/pkg/kclient"
"github.com/openshift/odo/pkg/occlient"

componentlabels "github.com/openshift/odo/pkg/component/labels"
"k8s.io/client-go/rest"

"fmt"
"net/http"

"github.com/openshift/odo/pkg/log"
"github.com/openshift/odo/pkg/util"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"

k8sgenclioptions "k8s.io/cli-runtime/pkg/genericclioptions"
Expand All @@ -20,18 +18,22 @@ import (

// DefaultPortForwarder implements the SPDY based port forwarder
type DefaultPortForwarder struct {
client *occlient.Client
client *occlient.Client
kClient *kclient.Client
k8sgenclioptions.IOStreams
componentName string
appName string
projectName string
}

func NewDefaultPortForwarder(componentName, appName string, client *occlient.Client, streams k8sgenclioptions.IOStreams) *DefaultPortForwarder {
func NewDefaultPortForwarder(componentName, appName string, projectName string, client *occlient.Client, kClient *kclient.Client, streams k8sgenclioptions.IOStreams) *DefaultPortForwarder {
return &DefaultPortForwarder{
client: client,
kClient: kClient,
IOStreams: streams,
componentName: componentName,
appName: appName,
projectName: projectName,
}
}

Expand All @@ -40,14 +42,30 @@ func NewDefaultPortForwarder(componentName, appName string, client *occlient.Cli
// stop Chan is used to stop port forwarding
// ready Chan is used to signal failure to the channel receiver
func (f *DefaultPortForwarder) ForwardPorts(portPair string, stopChan, readyChan chan struct{}) error {
conf, err := f.client.KubeConfig.ClientConfig()
if err != nil {
return err
}
var pod *corev1.Pod
var conf *rest.Config
var err error

pod, err := f.getPodUsingComponentName()
if err != nil {
return err
if f.kClient == nil {
conf, err = f.client.KubeConfig.ClientConfig()
if err != nil {
return err
}

pod, err = f.client.GetPodUsingComponentName(f.componentName, f.appName)
if err != nil {
return err
}
} else {
conf, err = f.kClient.KubeConfig.ClientConfig()
if err != nil {
return err
}

pod, err = f.kClient.GetPodUsingComponentName(f.componentName)
if err != nil {
return err
}
}

if pod.Status.Phase != corev1.PodRunning {
Expand All @@ -58,7 +76,14 @@ func (f *DefaultPortForwarder) ForwardPorts(portPair string, stopChan, readyChan
if err != nil {
return err
}
req := f.client.BuildPortForwardReq(pod.Name)

var req *rest.Request
if f.kClient == nil {
req = f.client.BuildPortForwardReq(pod.Name)
} else {
req = f.kClient.GeneratePortForwardReq(pod.Name)
}

dialer := spdy.NewDialer(upgrader, &http.Client{Transport: transport}, "POST", req.URL())
fw, err := portforward.New(dialer, []string{portPair}, stopChan, readyChan, f.Out, f.ErrOut)
if err != nil {
Expand All @@ -67,16 +92,3 @@ func (f *DefaultPortForwarder) ForwardPorts(portPair string, stopChan, readyChan
log.Info("Started port forwarding at ports -", portPair)
return fw.ForwardPorts()
}

func (f *DefaultPortForwarder) getPodUsingComponentName() (*corev1.Pod, error) {
componentLabels := componentlabels.GetLabels(f.componentName, f.appName, false)
componentSelector := util.ConvertLabelsToSelector(componentLabels)
dc, err := f.client.GetOneDeploymentConfigFromSelector(componentSelector)
if err != nil {
return nil, errors.Wrap(err, "unable to get deployment for component")
}
// Find Pod for component
podSelector := fmt.Sprintf("deploymentconfig=%s", dc.Name)

return f.client.GetOnePodFromSelector(podSelector)
}
34 changes: 34 additions & 0 deletions pkg/devfile/adapters/common/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,19 @@ func GetBuildCommand(data data.DevfileData, devfileBuildCmd string) (buildComman
return getCommand(data, string(DefaultDevfileBuildCommand), false)
}

// GetDebugCommand iterates through the components in the devfile and returns the build command
func GetDebugCommand(data data.DevfileData, devfileDebugCmd string) (buildCommand common.DevfileCommand, err error) {
if devfileDebugCmd != "" {
// a build command was specified so if it is not found then it is an error
buildCommand, err = getCommand(data, devfileDebugCmd, true)
} else {
// a build command was not specified so if it is not found then it is not an error
buildCommand, err = getCommand(data, string(DefaultDevfileDebugCommand), false)
}

return
}

// GetRunCommand iterates through the components in the devfile and returns the run command
func GetRunCommand(data data.DevfileData, devfileRunCmd string) (runCommand common.DevfileCommand, err error) {
if devfileRunCmd != "" {
Expand Down Expand Up @@ -193,3 +206,24 @@ func ValidateAndGetPushDevfileCommands(data data.DevfileData, devfileInitCmd, de

return pushDevfileCommands, nil
}

func ValidateAndGetDebugDevfileCommands(data data.DevfileData, devfileDebugCmd string) (pushDebugCommand common.DevfileCommand, err error) {
var emptyCommand common.DevfileCommand

isDebugCommandValid := false
debugCommand, debugCmdErr := GetDebugCommand(data, devfileDebugCmd)
if debugCmdErr == nil && !reflect.DeepEqual(emptyCommand, debugCommand) {
isDebugCommandValid = true
glog.V(3).Infof("Run command: %v", debugCommand.Name)
}

if !isDebugCommandValid {
commandErrors := ""
if debugCmdErr != nil {
commandErrors += debugCmdErr.Error()
}
return common.DevfileCommand{}, fmt.Errorf(commandErrors)
}

return debugCommand, nil
}
3 changes: 3 additions & 0 deletions pkg/devfile/adapters/common/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ type PushParameters struct {
DevfileInitCmd string // DevfileInitCmd takes the init command through the command line and overwrites devfile init command
DevfileBuildCmd string // DevfileBuildCmd takes the build command through the command line and overwrites devfile build command
DevfileRunCmd string // DevfileRunCmd takes the run command through the command line and overwrites devfile run command
DevfileDebugCmd string // DevfileDebugCmd takes the debug command through the command line and overwrites the devfile debug command
EnvSpecificInfo envinfo.EnvSpecificInfo // EnvSpecificInfo contains infomation of env.yaml file
Debug bool // Runs the component in debug mode
DebugPort int // Port used for remote debugging
}

// SyncParameters is a struct containing the parameters to be used when syncing a devfile component
Expand Down
12 changes: 12 additions & 0 deletions pkg/devfile/adapters/common/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ const (
// DefaultDevfileRunCommand is a predefined devfile command for run
DefaultDevfileRunCommand PredefinedDevfileCommands = "devrun"

// DefaultDevfileDebugCommand is a predefined devfile command for build
DefaultDevfileDebugCommand PredefinedDevfileCommands = "devdebug"

// SupervisordInitContainerName The init container name for supervisord
SupervisordInitContainerName = "copy-supervisord"

Expand Down Expand Up @@ -66,6 +69,15 @@ const (
// EnvOdoCommandRun is the env defined in the runtime component container which holds the run command to be executed
EnvOdoCommandRun = "ODO_COMMAND_RUN"

// EnvOdoCommandDebugWorkingDir is the env defined in the runtime component container which holds the work dir for the debug command
EnvOdoCommandDebugWorkingDir = "ODO_COMMAND_DEBUG_WORKING_DIR"

// EnvOdoCommandDebug is the env defined in the runtime component container which holds the debug command to be executed
EnvOdoCommandDebug = "ODO_COMMAND_DEBUG"

// EnvDebugPort is the env defined in the runtime component container which holds the debug port for remote debugging
EnvDebugPort = "DEBUG_PORT"

// ShellExecutable is the shell executable
ShellExecutable = "/bin/sh"

Expand Down
Loading

0 comments on commit e362c28

Please sign in to comment.