Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds debug command for devfiles #3059

Merged
merged 11 commits into from
Jun 19, 2020
6 changes: 5 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,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
mik-dass marked this conversation as resolved.
Show resolved Hide resolved
- odo logout

# Run service-catalog e2e tests
Expand Down Expand Up @@ -168,7 +169,7 @@ jobs:
# Run devfile integration test on Kubernetes cluster
- <<: *base-test
stage: test
name: "devfile catalog, watch, push, delete and create command integration tests on kubernetes cluster"
name: "devfile catalog, watch, push, debug, delete and create command integration tests on kubernetes cluster"
env:
- MINIKUBE_WANTUPDATENOTIFICATION=false
- MINIKUBE_WANTREPORTERRORPROMPT=false
Expand All @@ -184,6 +185,8 @@ jobs:
- touch $KUBECONFIG
- sudo minikube start --vm-driver=none --kubernetes-version=v1.16.0
- "sudo chown -R travis: /home/travis/.minikube/"
- sudo apt-get -qq update
- sudo apt-get install -y socat
script:
- kubectl cluster-info
- make bin
Expand All @@ -192,5 +195,6 @@ jobs:
- travis_wait make test-cmd-devfile-catalog
- travis_wait make test-cmd-devfile-watch
- travis_wait make test-cmd-devfile-push
- travis_wait make test-cmd-devfile-debug
- travis_wait make test-cmd-devfile-delete
- travis_wait make test-cmd-devfile-create
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,12 @@ 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/
ginkgo $(GINKGO_FLAGS_SERIAL) -focus="odo devfile debug command serial tests" tests/integration/devfile/debug

# 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 @@ -11,7 +11,6 @@ import (
"strings"
"syscall"

"github.com/openshift/odo/pkg/occlient"
"github.com/openshift/odo/pkg/testingutil/filesystem"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/klog"
Expand All @@ -24,18 +23,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 @@ -66,7 +70,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 @@ -81,7 +85,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 @@ -102,7 +106,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 {
klog.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
68 changes: 40 additions & 28 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,34 +18,54 @@ 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,
}
}

// ForwardPorts forwards the port using the url for the remote pod.
// portPair is a pair of port in format "localPort:RemotePort" that is to be forwarded
// 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
}
func (f *DefaultPortForwarder) ForwardPorts(portPair string, stopChan, readyChan chan struct{}, isExperimental bool) error {
var pod *corev1.Pod
var conf *rest.Config
var err error

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

pod, err = f.kClient.GetPodUsingComponentName(f.componentName)
if err != nil {
return err
}
} else {
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
}
}

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 && isExperimental {
req = f.kClient.GeneratePortForwardReq(pod.Name)
} else {
req = f.client.BuildPortForwardReq(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)
}
27 changes: 27 additions & 0 deletions pkg/devfile/adapters/common/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ func GetBuildCommand(data data.DevfileData, devfileBuildCmd string) (buildComman
return getCommand(data, devfileBuildCmd, common.BuildCommandGroupType)
}

// GetDebugCommand iterates through the components in the devfile and returns the debug command
func GetDebugCommand(data data.DevfileData, devfileDebugCmd string) (debugCommand common.DevfileCommand, err error) {
return getCommand(data, devfileDebugCmd, common.DebugCommandGroupType)
}

// 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) {

Expand Down Expand Up @@ -209,3 +214,25 @@ func updateGroupforCommand(groupType common.DevfileCommandGroupType, command com
}
return command
}

// ValidateAndGetDebugDevfileCommands validates the debug command
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
klog.V(3).Infof("Debug command: %v", debugCommand.Exec.Id)
}

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

return debugCommand, nil
}
Loading