Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions USERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ Organizations below are **officially** using Argo Rollouts. Please send a PR wit
1. [Twilio SendGrid](https://sendgrid.com)
1. [Ubie](https://ubie.life/)
1. [UiPath](https://uipath.com)
1. [Unity](https://unity.com)
1. [Verkada](https://verkada.com)
1. [VGS](https://www.vgs.io)
1. [VISITS Technologies](https://visits.world/en)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@ kubectl argo rollouts dashboard

# Start UI dashboard on a specific port
kubectl argo rollouts dashboard --port 8080

# Start UI dashboard with client auth mode (requires bearer token)
kubectl argo rollouts dashboard --auth-mode client
```

## Options

```
--auth-mode string authentication mode: "server" (default, uses server credentials) or "client" (requires bearer token from users) (default "server")
-h, --help help for dashboard
-p, --port int port to listen on (default 3100)
--root-path string changes the root path of the dashboard (default "rollouts")
Expand Down
21 changes: 20 additions & 1 deletion pkg/kubectl-argo-rollouts/cmd/dashboard/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dashboard

import (
"context"
"fmt"

"github.com/spf13/cobra"

Expand All @@ -15,17 +16,25 @@ var (
%[1]s dashboard

# Start UI dashboard on a specific port
%[1]s dashboard --port 8080`
%[1]s dashboard --port 8080

# Start UI dashboard with client auth mode (requires bearer token)
%[1]s dashboard --auth-mode client`
)

func NewCmdDashboard(o *options.ArgoRolloutsOptions) *cobra.Command {
var rootPath string
var port int
var authMode string
var cmd = &cobra.Command{
Use: "dashboard",
Short: "Start UI dashboard",
Example: o.Example(dashBoardExample),
RunE: func(c *cobra.Command, args []string) error {
if authMode != server.AuthModeServer && authMode != server.AuthModeClient {
return fmt.Errorf("invalid auth mode %q: must be %q or %q", authMode, server.AuthModeServer, server.AuthModeClient)
}

namespace := o.Namespace()
kubeclientset := o.KubeClientset()
rolloutclientset := o.RolloutsClientset()
Expand All @@ -36,6 +45,15 @@ func NewCmdDashboard(o *options.ArgoRolloutsOptions) *cobra.Command {
RolloutsClientset: rolloutclientset,
DynamicClientset: o.DynamicClientset(),
RootPath: rootPath,
AuthMode: authMode,
}

if authMode == server.AuthModeClient {
restConfig, err := o.RESTClientGetter.ToRESTConfig()
if err != nil {
return fmt.Errorf("failed to get REST config: %w", err)
}
opts.RESTConfig = restConfig
}

for {
Expand All @@ -49,6 +67,7 @@ func NewCmdDashboard(o *options.ArgoRolloutsOptions) *cobra.Command {
}
cmd.Flags().StringVar(&rootPath, "root-path", "rollouts", "changes the root path of the dashboard")
cmd.Flags().IntVarP(&port, "port", "p", 3100, "port to listen on")
cmd.Flags().StringVar(&authMode, "auth-mode", server.AuthModeServer, `authentication mode: "server" (default, uses server credentials) or "client" (requires bearer token from users)`)

return cmd
}
83 changes: 83 additions & 0 deletions pkg/kubectl-argo-rollouts/cmd/dashboard/dashboard_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package dashboard

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/discovery"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"

options "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/options"
fakeoptions "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/options/fake"
)

// failingRESTConfigGetter wraps a RESTClientGetter and overrides ToRESTConfig to return an error
type failingRESTConfigGetter struct {
delegate genericclioptions.RESTClientGetter
}

func (f *failingRESTConfigGetter) ToRESTConfig() (*rest.Config, error) {
return nil, fmt.Errorf("mock REST config error")
}

func (f *failingRESTConfigGetter) ToRawKubeConfigLoader() clientcmd.ClientConfig {
return f.delegate.ToRawKubeConfigLoader()
}

func (f *failingRESTConfigGetter) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
return f.delegate.ToDiscoveryClient()
}

func (f *failingRESTConfigGetter) ToRESTMapper() (meta.RESTMapper, error) {
return f.delegate.ToRESTMapper()
}

func TestNewCmdDashboard(t *testing.T) {
streams := genericclioptions.IOStreams{}
o := options.NewArgoRolloutsOptions(streams)

t.Run("default auth mode is server", func(t *testing.T) {
cmd := NewCmdDashboard(o)
f := cmd.Flags().Lookup("auth-mode")

Check failure on line 45 in pkg/kubectl-argo-rollouts/cmd/dashboard/dashboard_test.go

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "auth-mode" 3 times.

See more on https://sonarcloud.io/project/issues?id=argoproj_argo-rollouts&issues=AZ2Let7VD9YzqOqJOb1V&open=AZ2Let7VD9YzqOqJOb1V&pullRequest=4690
assert.NotNil(t, f)
assert.Equal(t, "server", f.DefValue)
})

t.Run("has port flag", func(t *testing.T) {
cmd := NewCmdDashboard(o)
f := cmd.Flags().Lookup("port")
assert.NotNil(t, f)
assert.Equal(t, "3100", f.DefValue)
})

t.Run("has root-path flag", func(t *testing.T) {
cmd := NewCmdDashboard(o)
f := cmd.Flags().Lookup("root-path")
assert.NotNil(t, f)
assert.Equal(t, "rollouts", f.DefValue)
})

t.Run("rejects invalid auth mode", func(t *testing.T) {
cmd := NewCmdDashboard(o)
cmd.Flags().Set("auth-mode", "invalid")
err := cmd.RunE(cmd, []string{})
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid auth mode")
})
}

func TestDashboardClientAuthModeRESTConfigFailure(t *testing.T) {
tf, o := fakeoptions.NewFakeArgoRolloutsOptions()
defer tf.Cleanup()
// Wrap the RESTClientGetter so ToRESTConfig fails but other methods work
o.RESTClientGetter = &failingRESTConfigGetter{delegate: tf}
cmd := NewCmdDashboard(o)
cmd.Flags().Set("auth-mode", "client")
err := cmd.RunE(cmd, []string{})
assert.Error(t, err)
assert.Contains(t, err.Error(), "failed to get REST config")
}
Loading
Loading