From ac9ab1e7e456608d4181fb8b085d2a9baf16fba6 Mon Sep 17 00:00:00 2001 From: Philippe Martin Date: Thu, 29 Jun 2023 11:02:51 +0200 Subject: [PATCH] Implement /devstate/*command --- pkg/apiserver-impl/devstate.go | 74 ++++++++++++++++++++ pkg/apiserver-impl/devstate/commands.go | 91 +++++++++++++++++++++++++ pkg/apiserver-impl/devstate/content.go | 18 ++--- 3 files changed, 174 insertions(+), 9 deletions(-) create mode 100644 pkg/apiserver-impl/devstate/commands.go diff --git a/pkg/apiserver-impl/devstate.go b/pkg/apiserver-impl/devstate.go index e17d615fd11..673a2ade386 100644 --- a/pkg/apiserver-impl/devstate.go +++ b/pkg/apiserver-impl/devstate.go @@ -88,3 +88,77 @@ func (s *DefaultApiService) DevstateResourceResourceNameDelete(ctx context.Conte } return openapi.Response(http.StatusOK, newContent), nil } + +func (s *DefaultApiService) DevstateApplyCommandPost(ctx context.Context, command openapi.DevstateApplyCommandPostRequest) (openapi.ImplResponse, error) { + newContent, err := s.devfileState.AddApplyCommand( + command.Name, + command.Component, + ) + if err != nil { + return openapi.Response(http.StatusInternalServerError, openapi.GeneralError{ + Message: fmt.Sprintf("Error adding the Apply command: %s", err), + }), nil + } + return openapi.Response(http.StatusOK, newContent), nil +} + +func (s *DefaultApiService) DevstateApplyCommandCommandNameDelete(ctx context.Context, commandName string) (openapi.ImplResponse, error) { + newContent, err := s.devfileState.DeleteCommand(commandName) + if err != nil { + return openapi.Response(http.StatusInternalServerError, openapi.GeneralError{ + Message: fmt.Sprintf("Error deleting the Apply command: %s", err), + }), nil + } + return openapi.Response(http.StatusOK, newContent), nil +} + +func (s *DefaultApiService) DevstateCompositeCommandPost(ctx context.Context, command openapi.DevstateCompositeCommandPostRequest) (openapi.ImplResponse, error) { + newContent, err := s.devfileState.AddCompositeCommand( + command.Name, + command.Parallel, + command.Commands, + ) + if err != nil { + return openapi.Response(http.StatusInternalServerError, openapi.GeneralError{ + Message: fmt.Sprintf("Error adding the Composite command: %s", err), + }), nil + } + return openapi.Response(http.StatusOK, newContent), nil + +} + +func (s *DefaultApiService) DevstateCompositeCommandCommandNameDelete(ctx context.Context, commandName string) (openapi.ImplResponse, error) { + newContent, err := s.devfileState.DeleteCommand(commandName) + if err != nil { + return openapi.Response(http.StatusInternalServerError, openapi.GeneralError{ + Message: fmt.Sprintf("Error deleting the Composite command: %s", err), + }), nil + } + return openapi.Response(http.StatusOK, newContent), nil +} + +func (s *DefaultApiService) DevstateExecCommandPost(ctx context.Context, command openapi.DevstateExecCommandPostRequest) (openapi.ImplResponse, error) { + newContent, err := s.devfileState.AddExecCommand( + command.Name, + command.Component, + command.CommandLine, + command.WorkingDir, + command.HotReloadCapable, + ) + if err != nil { + return openapi.Response(http.StatusInternalServerError, openapi.GeneralError{ + Message: fmt.Sprintf("Error adding the Exec command: %s", err), + }), nil + } + return openapi.Response(http.StatusOK, newContent), nil +} + +func (s *DefaultApiService) DevstateExecCommandCommandNameDelete(ctx context.Context, commandName string) (openapi.ImplResponse, error) { + newContent, err := s.devfileState.DeleteCommand(commandName) + if err != nil { + return openapi.Response(http.StatusInternalServerError, openapi.GeneralError{ + Message: fmt.Sprintf("Error deleting the Exec command: %s", err), + }), nil + } + return openapi.Response(http.StatusOK, newContent), nil +} diff --git a/pkg/apiserver-impl/devstate/commands.go b/pkg/apiserver-impl/devstate/commands.go new file mode 100644 index 00000000000..b684984a6a3 --- /dev/null +++ b/pkg/apiserver-impl/devstate/commands.go @@ -0,0 +1,91 @@ +package devstate + +import ( + "fmt" + + "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" + "github.com/devfile/library/v2/pkg/devfile/parser/data/v2/common" +) + +func (o *DevfileState) AddExecCommand(name string, component string, commandLine string, workingDir string, hotReloadCapable bool) (DevfileContent, error) { + command := v1alpha2.Command{ + Id: name, + CommandUnion: v1alpha2.CommandUnion{ + Exec: &v1alpha2.ExecCommand{ + Component: component, + CommandLine: commandLine, + WorkingDir: workingDir, + HotReloadCapable: &hotReloadCapable, + }, + }, + } + err := o.Devfile.Data.AddCommands([]v1alpha2.Command{command}) + if err != nil { + return DevfileContent{}, err + } + return o.GetContent() +} + +func (o *DevfileState) AddApplyCommand(name string, component string) (DevfileContent, error) { + command := v1alpha2.Command{ + Id: name, + CommandUnion: v1alpha2.CommandUnion{ + Apply: &v1alpha2.ApplyCommand{ + Component: component, + }, + }, + } + err := o.Devfile.Data.AddCommands([]v1alpha2.Command{command}) + if err != nil { + return DevfileContent{}, err + } + return o.GetContent() +} + +func (o *DevfileState) AddCompositeCommand(name string, parallel bool, commands []string) (DevfileContent, error) { + command := v1alpha2.Command{ + Id: name, + CommandUnion: v1alpha2.CommandUnion{ + Composite: &v1alpha2.CompositeCommand{ + Parallel: ¶llel, + Commands: commands, + }, + }, + } + err := o.Devfile.Data.AddCommands([]v1alpha2.Command{command}) + if err != nil { + return DevfileContent{}, err + } + return o.GetContent() +} + +func (o *DevfileState) DeleteCommand(name string) (DevfileContent, error) { + err := o.checkCommandUsed(name) + if err != nil { + return DevfileContent{}, fmt.Errorf("error deleting command %q: %w", name, err) + } + err = o.Devfile.Data.DeleteCommand(name) + if err != nil { + return DevfileContent{}, err + } + return o.GetContent() +} + +func (o *DevfileState) checkCommandUsed(name string) error { + commands, err := o.Devfile.Data.GetCommands(common.DevfileOptions{ + CommandOptions: common.CommandOptions{ + CommandType: v1alpha2.CompositeCommandType, + }, + }) + if err != nil { + return err + } + for _, command := range commands { + for _, subcommand := range command.Composite.Commands { + if subcommand == name { + return fmt.Errorf("command %q is used by composite command %q", name, command.Id) + } + } + } + return nil +} diff --git a/pkg/apiserver-impl/devstate/content.go b/pkg/apiserver-impl/devstate/content.go index 6c1f4920b72..0b9173c2faf 100644 --- a/pkg/apiserver-impl/devstate/content.go +++ b/pkg/apiserver-impl/devstate/content.go @@ -46,10 +46,10 @@ type Command struct { Group string Default bool Type string - Exec ExecCommand - Apply ApplyCommand - Image ImageCommand - Composite CompositeCommand + Exec *ExecCommand + Apply *ApplyCommand + Image *ImageCommand + Composite *CompositeCommand } type ExecCommand struct { @@ -118,7 +118,7 @@ func (o *DevfileState) GetContent() (DevfileContent, error) { commands, err := o.getCommands() if err != nil { - return DevfileContent{}, errors.New("error getting commands") + return DevfileContent{}, fmt.Errorf("error getting commands: %w", err) } containers, err := o.getContainers() @@ -188,7 +188,7 @@ func (o *DevfileState) getCommands() ([]Command, error) { if command.Exec != nil { newCommand.Type = "exec" - newCommand.Exec = ExecCommand{ + newCommand.Exec = &ExecCommand{ Component: command.Exec.Component, CommandLine: command.Exec.CommandLine, WorkingDir: command.Exec.WorkingDir, @@ -209,13 +209,13 @@ func (o *DevfileState) getCommands() ([]Command, error) { component := components[0] if component.Kubernetes != nil || component.Openshift != nil { newCommand.Type = "apply" - newCommand.Apply = ApplyCommand{ + newCommand.Apply = &ApplyCommand{ Component: command.Apply.Component, } } if component.Image != nil { newCommand.Type = "image" - newCommand.Image = ImageCommand{ + newCommand.Image = &ImageCommand{ Component: command.Apply.Component, } } @@ -223,7 +223,7 @@ func (o *DevfileState) getCommands() ([]Command, error) { if command.Composite != nil { newCommand.Type = "composite" - newCommand.Composite = CompositeCommand{ + newCommand.Composite = &CompositeCommand{ Commands: command.Composite.Commands, Parallel: pointer.BoolDeref(command.Composite.Parallel, false), }