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

Display information about the running API Server and web UI in odo describe component output #6964

22 changes: 19 additions & 3 deletions pkg/api/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package api

// Component describes the state of a devfile component
type Component struct {
DevfilePath string `json:"devfilePath,omitempty"`
DevfileData *DevfileData `json:"devfileData,omitempty"`
DevForwardedPorts []ForwardedPort `json:"devForwardedPorts,omitempty"`
DevfilePath string `json:"devfilePath,omitempty"`
DevfileData *DevfileData `json:"devfileData,omitempty"`
DevControlPlane []DevControlPlane `json:"devControlPlane,omitempty"`
DevForwardedPorts []ForwardedPort `json:"devForwardedPorts,omitempty"`
// RunningIn is the overall running mode map of the component;
// this is computing as a merge of RunningOn (all the different running modes
// for each platform the component is running on).
Expand All @@ -29,6 +30,21 @@ type ForwardedPort struct {
Protocol string `json:"protocol,omitempty"`
}

func (o ForwardedPort) GetPlatform() string {
return o.Platform
}

type DevControlPlane struct {
Platform string `json:"platform,omitempty"`
LocalPort int `json:"localPort"`
APIServerPath string `json:"apiServerPath"`
WebInterfacePath string `json:"webInterfacePath"`
}

func (o DevControlPlane) GetPlatform() string {
return o.Platform
}

type ConnectionData struct {
Name string `json:"name"`
Rules []Rules `json:"rules,omitempty"`
Expand Down
75 changes: 48 additions & 27 deletions pkg/component/describe/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ import (
"github.com/redhat-developer/odo/pkg/state"
)

type platformDependent interface {
GetPlatform() string
}

// DescribeDevfileComponent describes the component defined by the devfile in the current directory
func DescribeDevfileComponent(
ctx context.Context,
Expand Down Expand Up @@ -64,6 +68,22 @@ func DescribeDevfileComponent(
kubeClient = nil
}

isApiServerFeatureEnabled := feature.IsEnabled(ctx, feature.APIServerFlag)
// TODO(feloy) Pass PID with `--pid` flag
allControlPlaneData, err := stateClient.GetAPIServerPorts(ctx)
if err != nil {
return api.Component{}, nil, err
}
if isApiServerFeatureEnabled {
for i := range allControlPlaneData {
if allControlPlaneData[i].Platform == "" {
allControlPlaneData[i].Platform = commonflags.PlatformCluster
}
}
}

devControlPlaneData := filterByPlatform(ctx, isApiServerFeatureEnabled, allControlPlaneData)

// TODO(feloy) Pass PID with `--pid` flag
allFwdPorts, err := stateClient.GetForwardedPorts(ctx)
if err != nil {
Expand All @@ -76,33 +96,7 @@ func DescribeDevfileComponent(
}
}
}
var forwardedPorts []api.ForwardedPort
switch platform {
case "":
if isPlatformFeatureEnabled {
// Read ports from all platforms
forwardedPorts = allFwdPorts
} else {
// Limit to cluster ports only
for _, p := range allFwdPorts {
if p.Platform == "" || p.Platform == commonflags.PlatformCluster {
forwardedPorts = append(forwardedPorts, p)
}
}
}
case commonflags.PlatformCluster:
for _, p := range allFwdPorts {
if p.Platform == "" || p.Platform == commonflags.PlatformCluster {
forwardedPorts = append(forwardedPorts, p)
}
}
case commonflags.PlatformPodman:
for _, p := range allFwdPorts {
if p.Platform == commonflags.PlatformPodman {
forwardedPorts = append(forwardedPorts, p)
}
}
}
forwardedPorts := filterByPlatform(ctx, isPlatformFeatureEnabled, allFwdPorts)

runningOn, err := GetRunningOn(ctx, componentName, kubeClient, podmanClient)
if err != nil {
Expand All @@ -122,6 +116,7 @@ func DescribeDevfileComponent(
cmp := api.Component{
DevfilePath: devfilePath,
DevfileData: devfileData,
DevControlPlane: devControlPlaneData,
DevForwardedPorts: forwardedPorts,
RunningIn: api.MergeRunningModes(runningOn),
RunningOn: runningOn,
Expand Down Expand Up @@ -234,6 +229,32 @@ func GetRunningOn(ctx context.Context, n string, kubeClient kclient.ClientInterf
return runningOn, nil
}

func filterByPlatform[T platformDependent](ctx context.Context, isFeatEnabled bool, all []T) (result []T) {
if !isFeatEnabled {
return nil
}

plt := fcontext.GetPlatform(ctx, "")
switch plt {
case "":
// Read from all platforms
result = all
case commonflags.PlatformCluster:
for _, p := range all {
if p.GetPlatform() == "" || p.GetPlatform() == commonflags.PlatformCluster {
result = append(result, p)
}
}
case commonflags.PlatformPodman:
for _, p := range all {
if p.GetPlatform() == commonflags.PlatformPodman {
result = append(result, p)
}
}
}
return result
}

func updateWithRemoteSourceLocation(cmp *api.Component) {
components, err := cmp.DevfileData.Devfile.GetComponents(common.DevfileOptions{
ComponentOptions: common.ComponentOptions{ComponentType: v1alpha2.ContainerComponentType},
Expand Down
89 changes: 89 additions & 0 deletions pkg/component/describe/describe_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package describe

import (
"context"
"testing"

"github.com/google/go-cmp/cmp"

fcontext "github.com/redhat-developer/odo/pkg/odo/commonflags/context"
)

type testType struct {
value string
platform string
}

var _ platformDependent = testType{}

func (c testType) GetPlatform() string {
return c.platform
}

func Test_filterByPlatform(t *testing.T) {
type args struct {
ctx context.Context
isFeatEnabled bool
}
type testCase struct {
name string
args args
wantResult []testType
}
allValues := []testType{
{value: "value without platform"},
{value: "value11 (cluster)", platform: "cluster"},
{value: "value12 (cluster)", platform: "cluster"},
{value: "value21 (podman)", platform: "podman"},
{value: "value22 (podman)", platform: "podman"},
}
tests := []testCase{
{
name: "feature disabled",
args: args{
ctx: context.Background(),
isFeatEnabled: false,
},
wantResult: nil,
},
{
name: "feature enabled and platform unset in context",
args: args{
ctx: context.Background(),
isFeatEnabled: true,
},
wantResult: allValues,
},
{
name: "feature enabled and platform set to cluster in context",
args: args{
ctx: fcontext.WithPlatform(context.Background(), "cluster"),
isFeatEnabled: true,
},
wantResult: []testType{
{"value without platform", ""},
{"value11 (cluster)", "cluster"},
{"value12 (cluster)", "cluster"},
},
},
{
name: "feature enabled and platform set to podman in context",
args: args{
ctx: fcontext.WithPlatform(context.Background(), "podman"),
isFeatEnabled: true,
},
wantResult: []testType{
{"value21 (podman)", "podman"},
{"value22 (podman)", "podman"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotResult := filterByPlatform(tt.args.ctx, tt.args.isFeatEnabled, allValues)
if diff := cmp.Diff(tt.wantResult, gotResult, cmp.AllowUnexported(testType{})); diff != "" {
t.Errorf("filterByPlatform() mismatch (-want +got):\n%s", diff)
}
})
}
}
13 changes: 13 additions & 0 deletions pkg/odo/cli/describe/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,19 @@ func printHumanReadableOutput(ctx context.Context, cmp api.Component, devfileObj
fmt.Println()
}

if feature.IsEnabled(ctx, feature.APIServerFlag) && len(cmp.DevControlPlane) != 0 {
const ctrlPlaneHost = "localhost"
log.Info("Dev Control Plane:")
for _, dcp := range cmp.DevControlPlane {
log.Printf(`%[1]s
API: http://%[2]s:%[3]d/%[4]s
Web UI: http://%[2]s:%[3]d/`,
log.Sbold(dcp.Platform),
ctrlPlaneHost, dcp.LocalPort, strings.TrimPrefix(dcp.APIServerPath, "/"))
}
fmt.Println()
}

if len(cmp.DevForwardedPorts) > 0 {
log.Info("Forwarded ports:")
for _, port := range cmp.DevForwardedPorts {
Expand Down
3 changes: 3 additions & 0 deletions pkg/state/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,7 @@ type Client interface {

// SetAPIServerPort sets the port where API server is listening in the state file and saves it to the file, updating the metadata
SetAPIServerPort(ctx context.Context, port int) error

// GetAPIServerPorts returns the port where the API servers are listening, possibly per platform.
GetAPIServerPorts(ctx context.Context) ([]api.DevControlPlane, error)
}
34 changes: 34 additions & 0 deletions pkg/state/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ func (o *State) SaveExit(ctx context.Context) error {
o.content.ForwardedPorts = nil
o.content.PID = 0
o.content.Platform = ""
o.content.APIServerPort = 0
err := o.delete(pid)
if err != nil {
return err
Expand All @@ -103,6 +104,39 @@ func (o *State) SetAPIServerPort(ctx context.Context, port int) error {
return o.save(ctx, pid)
}

func (o *State) GetAPIServerPorts(ctx context.Context) ([]api.DevControlPlane, error) {
var (
result []api.DevControlPlane
platforms []string
platform = fcontext.GetPlatform(ctx, "")
)
if platform == "" {
platforms = []string{commonflags.PlatformCluster, commonflags.PlatformPodman}
} else {
platforms = []string{platform}
}

for _, platform = range platforms {
content, err := o.read(platform)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
continue // if the state file does not exist, no API Servers are listening
}
return nil, err
}
if content.APIServerPort == 0 {
continue
}
result = append(result, api.DevControlPlane{
Platform: platform,
LocalPort: content.APIServerPort,
APIServerPath: "/api/v1/",
WebInterfacePath: "/",
})
}
return result, nil
}

// save writes the content structure in json format in file
func (o *State) save(ctx context.Context, pid int) error {

Expand Down
2 changes: 1 addition & 1 deletion pkg/state/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ type Content struct {
Platform string `json:"platform"`
// ForwardedPorts are the ports forwarded during odo dev session
ForwardedPorts []api.ForwardedPort `json:"forwardedPorts"`
APIServerPort int `json:"apiServerPort"`
APIServerPort int `json:"apiServerPort,omitempty"`
}
4 changes: 3 additions & 1 deletion tests/helper/helper_dev.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ func StartDevMode(options DevSessionOpts) (devSession DevSession, err error) {
return DevSession{}, err
}

env := append([]string{}, options.EnvVars...)
args := []string{"dev"}
if options.NoCommands {
args = append(args, "--no-commands")
Expand All @@ -163,6 +164,7 @@ func StartDevMode(options DevSessionOpts) (devSession DevSession, err error) {
args = append(args, "--address", options.CustomAddress)
}
if options.StartAPIServer {
env = append(env, "ODO_EXPERIMENTAL_MODE=true")
args = append(args, "--api-server")
if options.APIServerPort != 0 {
args = append(args, "--api-server-port", fmt.Sprintf("%d", options.APIServerPort))
Expand All @@ -180,7 +182,7 @@ func StartDevMode(options DevSessionOpts) (devSession DevSession, err error) {
cmd.Cmd.Stdout = c.Tty()
cmd.Cmd.Stderr = c.Tty()

session := cmd.AddEnv(options.EnvVars...).Runner().session
session := cmd.AddEnv(env...).Runner().session
timeoutInSeconds := 420
if options.TimeoutInSeconds != 0 {
timeoutInSeconds = options.TimeoutInSeconds
Expand Down
Loading