Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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 go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ require (
go.mongodb.org/mongo-driver v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.50.0 // indirect
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
golang.org/x/mod v0.34.0 // indirect
golang.org/x/net v0.53.0 // indirect
golang.org/x/oauth2 v0.27.0 // indirect
Expand Down
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
Expand Down Expand Up @@ -534,6 +536,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand Down Expand Up @@ -565,6 +569,8 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
Expand Down Expand Up @@ -641,6 +647,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
Expand Down Expand Up @@ -689,6 +697,8 @@ golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
golang.org/x/tools/go/expect v0.1.0-deprecated h1:jY2C5HGYR5lqex3gEniOQL0r7Dq5+VGVgY1nudX5lXY=
Expand Down
13 changes: 13 additions & 0 deletions internal/cmd/skupper/common/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ for other Kubernetes flavors, loadbalancer is the default.`

FlagNameReloadType = "reload-type"
FlagDescReloadType = "Specify the type of reload to perform. Choices: manual, auto"

FlagNameNetworkObserverUsername = "username"
FlagDescNetworkObserverUsername = "Username for HTTP Basic Auth"
FlagNameNetworkObserverPassword = "password"
FlagDescNetworkObserverPassword = "Password for HTTP Basic Auth (auto-generated if not provided)"
FlagNameNetworkObserverUninstall = "uninstall"
FlagDescNetworkObserverUninstall = "Uninstall Network Observer and related containers"
)

type CommandSiteCreateFlags struct {
Expand Down Expand Up @@ -245,6 +252,12 @@ type CommandListenerGenerateFlags struct {
Output string
}

type CommandNetworkObserverFlags struct {
Username string
Password string
Uninstall bool
}

type CommandVersionFlags struct {
Output string
}
Expand Down
39 changes: 39 additions & 0 deletions internal/cmd/skupper/system/kube/system_network-observer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package kube

import (
"fmt"

"github.com/skupperproject/skupper/internal/cmd/skupper/common"
"github.com/spf13/cobra"
)

type CmdSystemNetworkObserver struct {
CobraCmd *cobra.Command
Flags *common.CommandNetworkObserverFlags
namespace string
user string
password string
}

func NewCmdCmdSystemNetworkObserver() *CmdSystemNetworkObserver {
return &CmdSystemNetworkObserver{}
}

func (cmd *CmdSystemNetworkObserver) NewClient(cobraCommand *cobra.Command, args []string) {}

func (cmd *CmdSystemNetworkObserver) ValidateInput(args []string) error {
return nil
}

func (cmd *CmdSystemNetworkObserver) InputToOptions() {

}

func (cmd *CmdSystemNetworkObserver) Run() error {
fmt.Println("This command does not support kubernetes platforms.")
return nil
}

func (cmd *CmdSystemNetworkObserver) WaitUntil() error {
return nil
}
100 changes: 100 additions & 0 deletions internal/cmd/skupper/system/nonkube/system_network-observer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package nonkube

import (
"errors"
"fmt"

"github.com/skupperproject/skupper/internal/cmd/skupper/common"
networkobserver "github.com/skupperproject/skupper/internal/nonkube/network-observer"
"github.com/spf13/cobra"
)

type CmdSystemNetworkObserver struct {
CobraCmd *cobra.Command
Flags *common.CommandNetworkObserverFlags
namespace string
user string
password string
}

func NewCmdSystemNetworkObserver() *CmdSystemNetworkObserver {
return &CmdSystemNetworkObserver{}
}

func (cmd *CmdSystemNetworkObserver) NewClient(cobraCommand *cobra.Command, args []string) {
if cmd.CobraCmd != nil && cmd.CobraCmd.Flag(common.FlagNameNamespace) != nil {
cmd.namespace = cmd.CobraCmd.Flag(common.FlagNameNamespace).Value.String()
}
if cmd.namespace == "" {
cmd.namespace = "default"
}
}

func (cmd *CmdSystemNetworkObserver) ValidateInput(args []string) error {
var validationErrors []error

if len(args) > 0 {
validationErrors = append(validationErrors, fmt.Errorf("this command does not accept arguments"))
}

if cmd.Flags != nil && cmd.Flags.Uninstall {
if cmd.Flags.Password != "" {
validationErrors = append(validationErrors, fmt.Errorf("--%s cannot be used with --%s", common.FlagNameNetworkObserverPassword, common.FlagNameNetworkObserverUninstall))
}
}

return errors.Join(validationErrors...)
}

func (cmd *CmdSystemNetworkObserver) InputToOptions() {

if cmd.Flags.Username != "" {
cmd.user = cmd.Flags.Username
}

if cmd.Flags.Password != "" {
cmd.password = cmd.Flags.Password
}

}

func (cmd *CmdSystemNetworkObserver) Run() error {
installer, err := networkobserver.NewInstaller(cmd.namespace, cmd.user, cmd.password)
if err != nil {
return fmt.Errorf("failed to create installer: %w", err)
}

if cmd.Flags.Uninstall {

if err := installer.ValidatePrerequisitesForUninstall(); err != nil {
return err
}

if err := installer.Uninstall(); err != nil {
return fmt.Errorf("uninstallation failed: %w", err)
}

return nil
}

if err := installer.ValidatePrerequisitesForInstall(); err != nil {
return fmt.Errorf("prerequisite validation failed: %w", err)
}

result, err := installer.Install()
if err != nil {
return fmt.Errorf("installation failed: %w", err)
}

fmt.Println("Network observer installed successfully!")
fmt.Printf("\nAccess URL: %s\n", result.URL)
fmt.Printf("Username: %s\n", result.Username)
fmt.Printf("Password: %s\n", result.Password)
fmt.Println("\nNote: Save these credentials securely.")

return nil
}

func (cmd *CmdSystemNetworkObserver) WaitUntil() error {
return nil
}
156 changes: 156 additions & 0 deletions internal/cmd/skupper/system/nonkube/system_network-observer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package nonkube

import (
"strings"
"testing"

"github.com/skupperproject/skupper/internal/cmd/skupper/common"
"github.com/spf13/cobra"
)

func TestCmdSystemNetworkObserverValidateInput(t *testing.T) {
tests := []struct {
name string
args []string
flags *common.CommandNetworkObserverFlags
expectedError []string
}{
{
name: "rejects arguments",
args: []string{"extra"},
expectedError: []string{"this command does not accept arguments"},
},
{
name: "rejects credentials with uninstall",
flags: &common.CommandNetworkObserverFlags{
Uninstall: true,
Password: "password",
},
expectedError: []string{
"--password cannot be used with --uninstall",
},
},
{
name: "allows install credentials",
flags: &common.CommandNetworkObserverFlags{
Username: "user",
Password: "password",
},
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
cmd := &CmdSystemNetworkObserver{Flags: test.flags}

err := cmd.ValidateInput(test.args)
if len(test.expectedError) == 0 {
if err != nil {
t.Fatalf("expected no error, got %q", err.Error())
}
return
}

if err == nil {
t.Fatal("expected error")
}
for _, expected := range test.expectedError {
if !strings.Contains(err.Error(), expected) {
t.Fatalf("expected validation error %q, got %q", expected, err.Error())
}
}
})
}
}

func TestCmdSystemNetworkObserverNewClient(t *testing.T) {
tests := []struct {
name string
flagNamespace string
initialNamespace string
expectedNamespace string
}{
{
name: "uses namespace flag",
flagNamespace: "west",
expectedNamespace: "west",
},
{
name: "defaults namespace",
expectedNamespace: "default",
},
{
name: "defaults even when namespace was preset without flag",
initialNamespace: "east",
expectedNamespace: "default",
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
cobraCmd := &cobra.Command{}
cobraCmd.Flags().String(common.FlagNameNamespace, "", "")
if test.flagNamespace != "" {
if err := cobraCmd.Flags().Set(common.FlagNameNamespace, test.flagNamespace); err != nil {
t.Fatalf("failed to set namespace flag: %v", err)
}
}

cmd := &CmdSystemNetworkObserver{
CobraCmd: cobraCmd,
namespace: test.initialNamespace,
}

cmd.NewClient(cobraCmd, nil)

if cmd.namespace != test.expectedNamespace {
t.Fatalf("expected namespace %q, got %q", test.expectedNamespace, cmd.namespace)
}
})
}
}

func TestCmdSystemNetworkObserverInputToOptions(t *testing.T) {
tests := []struct {
name string
flags *common.CommandNetworkObserverFlags
initialUser string
initialPassword string
expectedUser string
expectedPassword string
}{
{
name: "copies credentials from flags",
flags: &common.CommandNetworkObserverFlags{Username: "user", Password: "password"},
expectedUser: "user",
expectedPassword: "password",
},
{
name: "leaves existing values when flags are empty",
flags: &common.CommandNetworkObserverFlags{},
initialUser: "existing-user",
initialPassword: "existing-password",
expectedUser: "existing-user",
expectedPassword: "existing-password",
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
cmd := &CmdSystemNetworkObserver{
Flags: test.flags,
user: test.initialUser,
password: test.initialPassword,
}

cmd.InputToOptions()

if cmd.user != test.expectedUser {
t.Fatalf("expected user %q, got %q", test.expectedUser, cmd.user)
}
if cmd.password != test.expectedPassword {
t.Fatalf("expected password %q, got %q", test.expectedPassword, cmd.password)
}
})
}
}
Loading
Loading