diff --git a/cmd/component_debug/down.go b/cmd/component_debug/down.go new file mode 100644 index 0000000..d6e246f --- /dev/null +++ b/cmd/component_debug/down.go @@ -0,0 +1,52 @@ +package component_debug + +import ( + "bunnyshell.com/cli/pkg/config" + "bunnyshell.com/cli/pkg/k8s/bridge" + "bunnyshell.com/cli/pkg/lib" + "bunnyshell.com/cli/pkg/debug_component/action" + "bunnyshell.com/cli/pkg/debug_component/action/down" + "github.com/spf13/cobra" +) + +func init() { + options := config.GetOptions() + settings := config.GetSettings() + + resourceLoader := bridge.NewResourceLoader() + downOptions := down.NewOptions(resourceLoader) + + command := &cobra.Command{ + Use: "stop", + + ValidArgsFunction: cobra.NoFileCompletions, + + PreRunE: lib.OnlyStylish, + + RunE: func(cmd *cobra.Command, args []string) error { + if err := resourceLoader.Load(settings.Profile); err != nil { + return err + } + + downParameters, err := downOptions.ToParameters() + if err != nil { + return err + } + + downAction := action.NewDown(*resourceLoader.Environment) + + return downAction.Run(downParameters) + }, + } + + flags := command.Flags() + + flags.AddFlag(options.Organization.GetFlag("organization")) + flags.AddFlag(options.Project.GetFlag("project")) + flags.AddFlag(options.Environment.GetFlag("environment")) + flags.AddFlag(options.ServiceComponent.GetFlag("component")) + + downOptions.UpdateFlagSet(command, flags) + + mainCmd.AddCommand(command) +} diff --git a/cmd/component_debug/root.go b/cmd/component_debug/root.go new file mode 100644 index 0000000..78bbeb8 --- /dev/null +++ b/cmd/component_debug/root.go @@ -0,0 +1,21 @@ +package component_debug + +import ( + "bunnyshell.com/cli/pkg/config" + "github.com/spf13/cobra" +) + +var mainCmd = &cobra.Command{ + Use: "debug", + Aliases: []string{"debug"}, + + Short: "Debug Component", +} + +func init() { + config.MainManager.CommandWithAPI(mainCmd) +} + +func GetMainCommand() *cobra.Command { + return mainCmd +} diff --git a/cmd/component_debug/ssh.go b/cmd/component_debug/ssh.go new file mode 100644 index 0000000..be2aa9d --- /dev/null +++ b/cmd/component_debug/ssh.go @@ -0,0 +1,19 @@ +package component_debug + +import ( + "github.com/spf13/pflag" +) + +type SSHOptions struct { + Shell string + + NoTTY bool + NoBanner bool +} + +func (o *SSHOptions) UpdateFlagSet(flags *pflag.FlagSet) { + flags.StringVar(&o.Shell, "shell", o.Shell, "Shell to use") + + flags.BoolVar(&o.NoTTY, "no-tty", o.NoTTY, "Do not allocate a TTY") + flags.BoolVar(&o.NoBanner, "no-banner", o.NoBanner, "Do not show environment banner before ssh") +} diff --git a/cmd/component_debug/up.go b/cmd/component_debug/up.go new file mode 100644 index 0000000..38286b9 --- /dev/null +++ b/cmd/component_debug/up.go @@ -0,0 +1,111 @@ +package component_debug + +import ( + "fmt" + + "bunnyshell.com/cli/pkg/config" + "bunnyshell.com/cli/pkg/k8s/bridge" + "bunnyshell.com/cli/pkg/lib" + "bunnyshell.com/cli/pkg/debug_component/action" + upAction "bunnyshell.com/cli/pkg/debug_component/action/up" + "github.com/spf13/cobra" +) + +func init() { + options := config.GetOptions() + settings := config.GetSettings() + + sshOptions := SSHOptions{ + Shell: "/bin/sh", + } + + resourceLoader := bridge.NewResourceLoader() + upOptions := upAction.NewOptions(resourceLoader) + + command := &cobra.Command{ + Use: "start", + + ValidArgsFunction: cobra.NoFileCompletions, + + PreRunE: func(cmd *cobra.Command, args []string) error { + if err := upOptions.Validate(); err != nil { + return err + } + + return lib.OnlyStylish(cmd, args) + }, + + RunE: func(cmd *cobra.Command, args []string) error { + upOptions.SetCommand(args) + + if err := resourceLoader.Load(settings.Profile); err != nil { + return err + } + + upParameters, err := upOptions.ToParameters() + if err != nil { + return err + } + + upAction := action.NewUp(*resourceLoader.Environment) + + if err = upAction.Run(upParameters); err != nil { + return err + } + + selectedContainerName, err := upAction.GetSelectedContainerName() + if err != nil { + return err + } + + if err = startSSH(*resourceLoader.Component.Id, selectedContainerName, sshOptions, cmd, args); err != nil { + return fmt.Errorf("debug SSH exited with: %s", err) + } + + return upAction.Close() + }, + } + + flags := command.Flags() + + flags.AddFlag(options.Organization.GetFlag("organization")) + flags.AddFlag(options.Project.GetFlag("project")) + flags.AddFlag(options.Environment.GetFlag("environment")) + flags.AddFlag(options.ServiceComponent.GetFlag("component")) + + upOptions.UpdateFlagSet(command, flags) + + sshOptions.UpdateFlagSet(flags) + + mainCmd.AddCommand(command) +} + +func startSSH(componentId string, containerName string, sshOptions SSHOptions, cmd *cobra.Command, args []string) error { + proxyArgs := []string{ + "components", "ssh", + "--id", componentId, + } + + proxyArgs = append(proxyArgs, "--container", containerName) + + if sshOptions.Shell != "" { + proxyArgs = append(proxyArgs, "--shell", sshOptions.Shell) + } + + if sshOptions.NoBanner { + proxyArgs = append(proxyArgs, "--no-banner") + } + + if sshOptions.NoTTY { + proxyArgs = append(proxyArgs, "--no-tty") + } + + root := cmd.Root() + root.SetArgs(append(proxyArgs, args...)) + + if err := root.Execute(); err != nil { + return err + } + + return nil +} diff --git a/cmd/utils/root.go b/cmd/utils/root.go index 6f6cb79..137a8cc 100644 --- a/cmd/utils/root.go +++ b/cmd/utils/root.go @@ -2,6 +2,7 @@ package utils import ( "bunnyshell.com/cli/cmd/git" + "bunnyshell.com/cli/cmd/component_debug" "bunnyshell.com/cli/cmd/remote_development" "github.com/spf13/cobra" ) @@ -11,6 +12,7 @@ var mainCmd = &cobra.Command{} func init() { mainCmd.AddCommand(git.GetMainCommand()) mainCmd.AddCommand(remote_development.GetMainCommand()) + mainCmd.AddCommand(component_debug.GetMainCommand()) } func GetMainCommand() *cobra.Command { diff --git a/go.mod b/go.mod index ef0d66d..1ec1a86 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ toolchain go1.23.2 replace github.com/imdario/mergo => github.com/imdario/mergo v0.3.16 require ( - bunnyshell.com/dev v0.6.0 + bunnyshell.com/dev v0.7.0 bunnyshell.com/sdk v0.20.0 github.com/AlecAivazis/survey/v2 v2.3.7 github.com/MakeNowJust/heredoc v1.0.0 @@ -19,7 +19,6 @@ require ( github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.19.0 github.com/thediveo/enumflag/v2 v2.0.5 - golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.30.2 k8s.io/apimachinery v0.30.2 @@ -90,6 +89,7 @@ require ( go.starlark.net v0.0.0-20240520160348-046347dcd104 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.24.0 // indirect + golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect golang.org/x/net v0.26.0 // indirect golang.org/x/oauth2 v0.21.0 // indirect golang.org/x/sync v0.7.0 // indirect diff --git a/go.sum b/go.sum index d34ce4e..cf99c2f 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -bunnyshell.com/dev v0.6.0 h1:JywvLrzxYKgnxegPByh+ET7CwFme6JVeJpWzRuqQTWc= -bunnyshell.com/dev v0.6.0/go.mod h1:+Xk46UXX9AW0nHrFMdO/IwpUPfALrck1/qI+LIXsDmE= +bunnyshell.com/dev v0.7.0 h1:O66E0uTWSjx5ARI4Gyb19hmk9NX4uhFoyEP/nm/8kHY= +bunnyshell.com/dev v0.7.0/go.mod h1:+Xk46UXX9AW0nHrFMdO/IwpUPfALrck1/qI+LIXsDmE= bunnyshell.com/sdk v0.20.0 h1:xcoDn0x1JqexMy5vYGqTBK4DGdY3juc+5DU7vb1yFeA= bunnyshell.com/sdk v0.20.0/go.mod h1:RfgfUzZ4WHZGCkToUfu2/hoQS6XsQc8IdPTVAlpS138= github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= diff --git a/pkg/api/event/list.go b/pkg/api/event/list.go index 62529c3..009fcaa 100644 --- a/pkg/api/event/list.go +++ b/pkg/api/event/list.go @@ -75,7 +75,7 @@ func applyOptions(request sdk.ApiEventListRequest, options *ListOptions) sdk.Api } if options.Status != "" { - request.Status(options.Status) + request = request.Status(options.Status) } return request diff --git a/pkg/debug_component/action/action.go b/pkg/debug_component/action/action.go new file mode 100644 index 0000000..689c90d --- /dev/null +++ b/pkg/debug_component/action/action.go @@ -0,0 +1,46 @@ +package action + +import ( + "errors" + "fmt" + + "bunnyshell.com/cli/pkg/remote_development/workspace" + "bunnyshell.com/dev/pkg/debug" + "bunnyshell.com/sdk" +) + +var ErrResourceKindNotSupported = errors.New("resource kind not supported") + +type Action struct { + workspace *workspace.Workspace +} + +func NewAction( + environment sdk.EnvironmentItem, +) *Action { + return &Action{ + workspace: workspace.NewWorkspace(environment.GetId()), + } +} + +func (action *Action) GetDebugCmp(resource sdk.ComponentResourceItem) (*debug.DebugComponent, error) { + kubeConfigFile, err := action.workspace.DownloadKubeConfig() + if err != nil { + return nil, err + } + + debugCmp := debug.NewDebugComponent(). + WithKubernetesClient(kubeConfigFile). + WithNamespaceName(resource.GetNamespace()) + + switch kind := resource.GetKind(); kind { + case "Deployment": + return debugCmp.WithDeploymentName(resource.GetName()), nil + case "StatefulSet": + return debugCmp.WithStatefulSetName(resource.GetName()), nil + case "DaemonSet": + return debugCmp.WithDaemonSetName(resource.GetName()), nil + default: + return nil, fmt.Errorf("%w: %s", ErrResourceKindNotSupported, kind) + } +} diff --git a/pkg/debug_component/action/down.go b/pkg/debug_component/action/down.go new file mode 100644 index 0000000..6ba1607 --- /dev/null +++ b/pkg/debug_component/action/down.go @@ -0,0 +1,30 @@ +package action + +import ( + "bunnyshell.com/sdk" +) + +type DownParameters struct { + Resource sdk.ComponentResourceItem +} + +type Down struct { + Action +} + +func NewDown( + environment sdk.EnvironmentItem, +) *Down { + return &Down{ + Action: *NewAction(environment), + } +} + +func (down *Down) Run(parameters *DownParameters) error { + debugCmp, err := down.Action.GetDebugCmp(parameters.Resource) + if err != nil { + return err + } + + return debugCmp.Down() +} diff --git a/pkg/debug_component/action/down/down.cobra.go b/pkg/debug_component/action/down/down.cobra.go new file mode 100644 index 0000000..ce06f78 --- /dev/null +++ b/pkg/debug_component/action/down/down.cobra.go @@ -0,0 +1,13 @@ +package down + +import ( + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +func (down *Options) UpdateFlagSet( + command *cobra.Command, + flags *pflag.FlagSet, +) { + flags.StringVarP(&down.resourcePath, "resource", "s", down.resourcePath, "The cluster resource to use (namespace/kind/name format).") +} diff --git a/pkg/debug_component/action/down/down.go b/pkg/debug_component/action/down/down.go new file mode 100644 index 0000000..8b1e33e --- /dev/null +++ b/pkg/debug_component/action/down/down.go @@ -0,0 +1,52 @@ +package down + +import ( + "bunnyshell.com/cli/pkg/k8s/bridge" + "bunnyshell.com/cli/pkg/debug_component/action" +) + +type Options struct { + ManualSelectSingleResource bool + + resourceLoader *bridge.ResourceLoader + + resourcePath string +} + +func NewOptions( + resourceLoader *bridge.ResourceLoader, +) *Options { + return &Options{ + resourceLoader: resourceLoader, + } +} + +func (down *Options) ToParameters() (*action.DownParameters, error) { + down.resourceLoader.ManualSelectSingleResource = down.ManualSelectSingleResource + + if err := down.loadResource(); err != nil { + return nil, err + } + + parameters := &action.DownParameters{ + Resource: *down.resourceLoader.GetResource(), + } + + return parameters, nil +} + +func (down *Options) loadResource() error { + if !down.resourceLoader.IsLoaded() { + return ErrResourceLoaderNotHydrated + } + + if down.resourceLoader.GetResource() != nil { + return nil + } + + if down.resourcePath != "" { + return down.resourceLoader.SelectResourceFromString(down.resourcePath) + } + + return down.resourceLoader.SelectResource() +} diff --git a/pkg/debug_component/action/down/vars.go b/pkg/debug_component/action/down/vars.go new file mode 100644 index 0000000..12647d3 --- /dev/null +++ b/pkg/debug_component/action/down/vars.go @@ -0,0 +1,7 @@ +package down + +import ( + "errors" +) + +var ErrResourceLoaderNotHydrated = errors.New("resourceLoader needs to be hydrated") diff --git a/pkg/debug_component/action/up.go b/pkg/debug_component/action/up.go new file mode 100644 index 0000000..6c1a357 --- /dev/null +++ b/pkg/debug_component/action/up.go @@ -0,0 +1,163 @@ +package action + +import ( + "bunnyshell.com/dev/pkg/debug" + "bunnyshell.com/dev/pkg/remote/container" + "bunnyshell.com/sdk" +) + +type UpOptions struct { + ContainerName string + + EnvironPairs []string + + Command []string + + LimitCPU string + LimitMemory string + + RequestCPU string + RequestMemory string + + WaitTimeout int64 +} + +type UpParameters struct { + Resource sdk.ComponentResourceItem + + ManualSelectSingleResource bool + + ForceRecreateResource bool + + Options *UpOptions +} + +func (params *UpParameters) FillFromOptions() error { + if params.Options == nil { + return nil + } + + return nil +} + +type Up struct { + Action + + debugCmp *debug.DebugComponent +} + +func NewUp( + environment sdk.EnvironmentItem, +) *Up { + return &Up{ + Action: *NewAction(environment), + } +} + +func (up *Up) Run(parameters *UpParameters) error { + debugCmp, err := up.Action.GetDebugCmp(parameters.Resource) + if err != nil { + return err + } + + return up.run(debugCmp, parameters) +} + +func (up *Up) Wait() error { + if up.debugCmp == nil { + return ErrDebugCmpNotInitialized + } + + return up.debugCmp.Wait() +} + +func (up *Up) Close() error { + if up.debugCmp == nil { + return ErrDebugCmpNotInitialized + } + + up.debugCmp.Close() + + return nil +} + +func (up *Up) run( + debugCmp *debug.DebugComponent, + parameters *UpParameters, +) error { + if err := up.loadDebugCmpOptions(debugCmp, parameters.Options); err != nil { + return err + } + + if err := debugCmp.SelectContainer(); err != nil { + return err + } + + if err := debugCmp.CanUp(parameters.ForceRecreateResource); err != nil { + return err + } + + up.debugCmp = debugCmp + + return debugCmp.Up() +} + +func (up *Up) loadDebugCmpOptions(debugCmp *debug.DebugComponent, options *UpOptions) error { + if options == nil { + return nil + } + + debugCmp.ContainerName = options.ContainerName + + if options.WaitTimeout != 0 { + debugCmp.WithWaitTimeout(options.WaitTimeout) + } + + up.setContainerResources(&debugCmp.ContainerConfig, options) + + if len(options.EnvironPairs) > 0 { + for _, pair := range options.EnvironPairs { + if err := debugCmp.ContainerConfig.Environ.AddFromDefinition(pair); err != nil { + return err + } + } + } + + return nil +} + +func (up *Up) setContainerResources(containerConfig *container.Config, options *UpOptions) error { + if options.RequestCPU != "" { + if err := containerConfig.Resources.SetRequestsCPU(options.RequestCPU); err != nil { + return err + } + } + + if options.RequestMemory != "" { + if err := containerConfig.Resources.SetRequestsMemory(options.RequestMemory); err != nil { + return err + } + } + + if options.LimitCPU != "" { + if err := containerConfig.Resources.SetLimitsCPU(options.LimitCPU); err != nil { + return err + } + } + + if options.LimitMemory != "" { + if err := containerConfig.Resources.SetLimitsMemory(options.LimitMemory); err != nil { + return err + } + } + + return nil +} + +func (up *Up) GetSelectedContainerName() (string, error) { + if up.debugCmp == nil { + return "", ErrDebugCmpNotInitialized + } + + return up.debugCmp.GetSelectedContainerName() +} \ No newline at end of file diff --git a/pkg/debug_component/action/up/up.cobra.go b/pkg/debug_component/action/up/up.cobra.go new file mode 100644 index 0000000..e82f408 --- /dev/null +++ b/pkg/debug_component/action/up/up.cobra.go @@ -0,0 +1,45 @@ +package up + +import ( + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +func (up *Options) UpdateFlagSet( + command *cobra.Command, + flags *pflag.FlagSet, +) { + flags.BoolVar( + &up.ManualSelectSingleResource, + "manual-select-single-resource", + up.ManualSelectSingleResource, + "Do not skip interactive selectors when there is only one result", + ) + + _ = flags.MarkHidden("no-auto-select-one") + + flags.BoolVar( + &up.ForceRecreateResource, + "force-recreate-resource", + up.ForceRecreateResource, + "Force recreate Pod even if another debug session is in progress. May break the debug down command", + ) + + flags.DurationVarP(&up.waitTimeout, "wait-timeout", "w", up.waitTimeout, "Time to wait for the pod to be ready") + + flags.StringVarP(&up.resourcePath, "resource", "s", up.resourcePath, "The cluster resource to use (namespace/kind/name format).") + flags.StringVar(&up.containerName, "container", up.containerName, "The container name to use for remote development") + + up.addContainerConfigFlags(flags) +} + +func (up *Options) addContainerConfigFlags( + flags *pflag.FlagSet, +) { + flags.StringArrayVar(&up.environPairs, "env", up.environPairs, "Environment variables to set in the debugged container") + + flags.StringVar(&up.limitCPU, "limit-cpu", up.limitCPU, "CPU resource limits for the debugged container") + flags.StringVar(&up.limitMemory, "limit-memory", up.limitMemory, "Memory resource limits for the debugged container") + flags.StringVar(&up.requestCPU, "request-cpu", up.requestCPU, "CPU resource reservations for the debugged container") + flags.StringVar(&up.requestMemory, "request-memory", up.requestMemory, "Memory resource reservations for the debugged container") +} diff --git a/pkg/debug_component/action/up/up.go b/pkg/debug_component/action/up/up.go new file mode 100644 index 0000000..8382a08 --- /dev/null +++ b/pkg/debug_component/action/up/up.go @@ -0,0 +1,120 @@ +package up + +import ( + "time" + + "bunnyshell.com/cli/pkg/k8s/bridge" + "bunnyshell.com/cli/pkg/debug_component/action" +) + +type Options struct { + ManualSelectSingleResource bool + + ForceRecreateResource bool + + resourceLoader *bridge.ResourceLoader + + waitTimeout time.Duration + + resourcePath string + containerName string + + command []string + + environPairs []string + + limitCPU string + limitMemory string + + requestCPU string + requestMemory string +} + +func NewOptions( + resourceLoader *bridge.ResourceLoader, +) *Options { + return &Options{ + resourceLoader: resourceLoader, + + waitTimeout: defaultWaitTimeout, + } +} + +func (up *Options) SetCommand(command []string) { + up.command = command +} + +func (up *Options) Validate() error { + return nil +} + +func (up *Options) ToParameters() (*action.UpParameters, error) { + up.resourceLoader.ManualSelectSingleResource = up.ManualSelectSingleResource + + parameters := &action.UpParameters{ + ManualSelectSingleResource: up.ManualSelectSingleResource, + ForceRecreateResource: up.ForceRecreateResource, + + Options: &action.UpOptions{ + WaitTimeout: int64(up.waitTimeout.Seconds()), + + EnvironPairs: up.environPairs, + }, + } + + up.fillFromFlags(parameters) + + if err := up.loadResource(); err != nil { + return nil, err + } + + parameters.Resource = *up.resourceLoader.GetResource() + + if err := parameters.FillFromOptions(); err != nil { + return nil, err + } + + return parameters, nil +} + +func (up *Options) loadResource() error { + if !up.resourceLoader.IsLoaded() { + return ErrResourceLoaderNotHydrated + } + + if up.resourceLoader.GetResource() != nil { + return nil + } + + if up.resourcePath != "" { + return up.resourceLoader.SelectResourceFromString(up.resourcePath) + } + + return up.resourceLoader.SelectResource() +} + +func (up *Options) fillFromFlags(parameters *action.UpParameters) { + if up.limitCPU != "" { + parameters.Options.LimitCPU = up.limitCPU + } + + if up.limitMemory != "" { + parameters.Options.LimitMemory = up.limitMemory + } + + if up.requestCPU != "" { + parameters.Options.RequestCPU = up.requestCPU + } + + if up.requestMemory != "" { + parameters.Options.RequestMemory = up.requestMemory + } + + if up.containerName != "" { + parameters.Options.ContainerName = up.containerName + } + + if len(up.command) > 0 { + parameters.Options.Command = up.command + } +} diff --git a/pkg/debug_component/action/up/vars.go b/pkg/debug_component/action/up/vars.go new file mode 100644 index 0000000..6c29c81 --- /dev/null +++ b/pkg/debug_component/action/up/vars.go @@ -0,0 +1,12 @@ +package up + +import ( + "errors" + "time" +) + +const defaultWaitTimeout = 120 * time.Second + +var ( + ErrResourceLoaderNotHydrated = errors.New("resourceLoader needs to be hydrated") +) diff --git a/pkg/debug_component/action/vars.go b/pkg/debug_component/action/vars.go new file mode 100644 index 0000000..a4c6bbc --- /dev/null +++ b/pkg/debug_component/action/vars.go @@ -0,0 +1,7 @@ +package action + +import "errors" + +var ( + ErrDebugCmpNotInitialized = errors.New("call Up.Run() successfully before calling other methods") +) diff --git a/pkg/debug_component/debug_component.go b/pkg/debug_component/debug_component.go new file mode 100644 index 0000000..1a66305 --- /dev/null +++ b/pkg/debug_component/debug_component.go @@ -0,0 +1,54 @@ +package debug_component + +import ( + "fmt" + + "bunnyshell.com/cli/pkg/environment" + "bunnyshell.com/dev/pkg/debug" +) + +var ( + ErrNoOrganizationSelected = fmt.Errorf("you need to select an organization first") +) + +type DebugComponent struct { + debugCmp *debug.DebugComponent + environmentResource *environment.EnvironmentResource + + environmentWorkspaceDir string + + kubeConfigPath string + + waitTimeout int64 +} + +func NewDebugComponent() *DebugComponent { + return &DebugComponent{ + debugCmp: debug.NewDebugComponent(), + environmentResource: environment.NewEnvironmentResource(), + } +} + +func (d *DebugComponent) WithEnvironmentResource(environmentResource *environment.EnvironmentResource) *DebugComponent { + d.environmentResource = environmentResource + + return d +} + +func (d *DebugComponent) WithEnvironmentWorkspaceDir(environmentWorkspaceDir string) *DebugComponent { + d.environmentWorkspaceDir = environmentWorkspaceDir + + return d +} + +func (d *DebugComponent) WithKubeConfigPath(kubeConfigPath string) *DebugComponent { + d.kubeConfigPath = kubeConfigPath + + return d +} + +func (d *DebugComponent) WithWaitTimeout(waitTimeout int64) *DebugComponent { + d.waitTimeout = waitTimeout + + return d +} diff --git a/pkg/debug_component/main.go b/pkg/debug_component/main.go new file mode 100644 index 0000000..724ecca --- /dev/null +++ b/pkg/debug_component/main.go @@ -0,0 +1,72 @@ +package debug_component + +import ( + "fmt" +) + +func (d *DebugComponent) Up() error { + if err := d.ensureEnvironmentWorkspaceDir(); err != nil { + return err + } + + if err := d.ensureEnvironmentKubeConfig(); err != nil { + return err + } + + componentResource := d.environmentResource.ComponentResource + + d.debugCmp. + WithKubernetesClient(d.kubeConfigPath). + WithNamespaceName(componentResource.GetNamespace()). + WithWaitTimeout(d.waitTimeout) + + switch componentResource.GetKind() { + case "Deployment": + d.debugCmp.WithDeploymentName(componentResource.GetName()) + case "StatefulSet": + d.debugCmp.WithStatefulSetName(componentResource.GetName()) + case "DaemonSet": + d.debugCmp.WithDaemonSetName(componentResource.GetName()) + default: + return fmt.Errorf("resource kind \"%s\" is not supported", componentResource.GetKind()) + } + + if err := d.debugCmp.SelectContainer(); err != nil { + return err + } + + return d.debugCmp.Up() +} + +func (d *DebugComponent) Down() error { + if err := d.ensureEnvironmentWorkspaceDir(); err != nil { + return err + } + + if err := d.ensureEnvironmentKubeConfig(); err != nil { + return err + } + + componentResource := d.environmentResource.ComponentResource + + d.debugCmp. + WithKubernetesClient(d.kubeConfigPath). + WithNamespaceName(componentResource.GetNamespace()) + + switch componentResource.GetKind() { + case "Deployment": + d.debugCmp.WithDeploymentName(componentResource.GetName()) + case "StatefulSet": + d.debugCmp.WithStatefulSetName(componentResource.GetName()) + case "DaemonSet": + d.debugCmp.WithDaemonSetName(componentResource.GetName()) + default: + return fmt.Errorf("resource kind \"%s\" is not supported", componentResource.GetKind()) + } + + return d.debugCmp.Down() +} + +func (d *DebugComponent) Wait() error { + return d.debugCmp.Wait() +} diff --git a/pkg/debug_component/workspace.go b/pkg/debug_component/workspace.go new file mode 100644 index 0000000..507f7f6 --- /dev/null +++ b/pkg/debug_component/workspace.go @@ -0,0 +1,37 @@ +package debug_component + +import ( + "os" + "path/filepath" + + "bunnyshell.com/cli/pkg/lib" + "bunnyshell.com/cli/pkg/util" +) + +const ( + KubeConfigFilename = "kube-config.yaml" +) + +// @deprecated Use bunnyshell.com/cli/pkg/remote_development/workspace +func (d *DebugComponent) ensureEnvironmentWorkspaceDir() error { + workspace, err := util.GetWorkspaceDir() + if err != nil { + return err + } + + d.WithEnvironmentWorkspaceDir(filepath.Join(workspace, d.environmentResource.Environment.GetId())) + + return os.MkdirAll(d.environmentWorkspaceDir, 0755) +} + +// @deprecated Use bunnyshell.com/cli/pkg/remote_development/workspace +func (d *DebugComponent) ensureEnvironmentKubeConfig() error { + kubeConfigPath := filepath.Join(d.environmentWorkspaceDir, KubeConfigFilename) + if err := lib.DownloadEnvironmentKubeConfig(kubeConfigPath, d.environmentResource.Environment.GetId()); err != nil { + return err + } + + d.WithKubeConfigPath(kubeConfigPath) + + return nil +} diff --git a/pkg/remote_development/action/up.go b/pkg/remote_development/action/up.go index df77d18..2d268fb 100644 --- a/pkg/remote_development/action/up.go +++ b/pkg/remote_development/action/up.go @@ -125,6 +125,10 @@ func (up *Up) run( return err } + if err := remoteDev.CanUp(); err != nil { + return err + } + if err := remoteDev.PrepareSSHTunnels(parameters.PortMappings); err != nil { return err }