github.com/kubeshop/testkube@v1.17.23/internal/app/api/v1/executor.go (about) 1 package v1 2 3 import ( 4 "bytes" 5 "fmt" 6 "net/http" 7 8 "github.com/gofiber/fiber/v2" 9 "k8s.io/apimachinery/pkg/api/errors" 10 "k8s.io/apimachinery/pkg/util/yaml" 11 12 executorv1 "github.com/kubeshop/testkube-operator/api/executor/v1" 13 "github.com/kubeshop/testkube/pkg/api/v1/testkube" 14 "github.com/kubeshop/testkube/pkg/crd" 15 executorsmapper "github.com/kubeshop/testkube/pkg/mapper/executors" 16 ) 17 18 func (s TestkubeAPI) CreateExecutorHandler() fiber.Handler { 19 return func(c *fiber.Ctx) error { 20 errPrefix := "failed to create executor" 21 var executor executorv1.Executor 22 if string(c.Request().Header.ContentType()) == mediaTypeYAML { 23 executorSpec := string(c.Body()) 24 decoder := yaml.NewYAMLOrJSONDecoder(bytes.NewBufferString(executorSpec), len(executorSpec)) 25 if err := decoder.Decode(&executor); err != nil { 26 return s.Error(c, http.StatusBadRequest, fmt.Errorf("%s: could not parse yaml request: %w", errPrefix, err)) 27 } 28 } else { 29 var request testkube.ExecutorUpsertRequest 30 err := c.BodyParser(&request) 31 if err != nil { 32 return s.Error(c, http.StatusBadRequest, fmt.Errorf("%s: could not parse json request: %w", errPrefix, err)) 33 } 34 35 if c.Accepts(mediaTypeJSON, mediaTypeYAML) == mediaTypeYAML { 36 request.QuoteExecutorTextFields() 37 data, err := crd.GenerateYAML(crd.TemplateExecutor, []testkube.ExecutorUpsertRequest{request}) 38 return s.getCRDs(c, data, err) 39 } 40 41 executor = executorsmapper.MapAPIToCRD(request) 42 executor.Namespace = s.Namespace 43 } 44 45 created, err := s.ExecutorsClient.Create(&executor) 46 if err != nil { 47 return s.Error(c, http.StatusBadGateway, fmt.Errorf("%s: client could not create executor: %w", errPrefix, err)) 48 } 49 50 s.Events.Notify(testkube.NewEvent( 51 testkube.EventCreated, 52 testkube.EventResourceExecutor, 53 created.Name, 54 )) 55 56 c.Status(http.StatusCreated) 57 return c.JSON(created) 58 } 59 } 60 61 func (s TestkubeAPI) UpdateExecutorHandler() fiber.Handler { 62 return func(c *fiber.Ctx) error { 63 errPrefix := "failed to update executor" 64 var request testkube.ExecutorUpdateRequest 65 if string(c.Request().Header.ContentType()) == mediaTypeYAML { 66 var executor executorv1.Executor 67 executorSpec := string(c.Body()) 68 decoder := yaml.NewYAMLOrJSONDecoder(bytes.NewBufferString(executorSpec), len(executorSpec)) 69 if err := decoder.Decode(&executor); err != nil { 70 return s.Error(c, http.StatusBadRequest, fmt.Errorf("%s: could not parse yaml request: %w", errPrefix, err)) 71 } 72 73 request = executorsmapper.MapSpecToUpdate(&executor) 74 } else { 75 err := c.BodyParser(&request) 76 if err != nil { 77 return s.Error(c, http.StatusBadRequest, fmt.Errorf("%s: could not parse json request: %w", errPrefix, err)) 78 } 79 } 80 81 var name string 82 if request.Name != nil { 83 name = *request.Name 84 } 85 errPrefix = errPrefix + " " + name 86 // we need to get resource first and load its metadata.ResourceVersion 87 executor, err := s.ExecutorsClient.Get(name) 88 if err != nil { 89 if errors.IsNotFound(err) { 90 return s.Error(c, http.StatusNotFound, fmt.Errorf("%s: client found no executor: %w", errPrefix, err)) 91 } 92 93 return s.Error(c, http.StatusBadGateway, fmt.Errorf("%s: client could not get executor: %w", errPrefix, err)) 94 } 95 96 // map update executor but load spec only to not override metadata.ResourceVersion 97 executorSpec := executorsmapper.MapUpdateToSpec(request, executor) 98 99 updatedExecutor, err := s.ExecutorsClient.Update(executorSpec) 100 if err != nil { 101 return s.Error(c, http.StatusBadGateway, fmt.Errorf("%s: client could not update executor: %w", errPrefix, err)) 102 } 103 104 s.Events.Notify(testkube.NewEvent( 105 testkube.EventUpdated, 106 testkube.EventResourceExecutor, 107 updatedExecutor.Name, 108 )) 109 110 return c.JSON(updatedExecutor) 111 } 112 } 113 114 func (s TestkubeAPI) ListExecutorsHandler() fiber.Handler { 115 return func(c *fiber.Ctx) error { 116 errPrefix := "failed to list executors" 117 list, err := s.ExecutorsClient.List(c.Query("selector")) 118 if err != nil { 119 return s.Error(c, http.StatusBadGateway, fmt.Errorf("%s: client could not list executors: %w", errPrefix, err)) 120 } 121 122 if c.Accepts(mediaTypeJSON, mediaTypeYAML) == mediaTypeYAML { 123 results := []testkube.ExecutorUpsertRequest{} 124 for _, item := range list.Items { 125 result := executorsmapper.MapCRDToAPI(item) 126 result.QuoteExecutorTextFields() 127 results = append(results, result) 128 } 129 130 data, err := crd.GenerateYAML(crd.TemplateExecutor, results) 131 return s.getCRDs(c, data, err) 132 } 133 134 results := []testkube.ExecutorDetails{} 135 for _, item := range list.Items { 136 results = append(results, executorsmapper.MapExecutorCRDToExecutorDetails(item)) 137 138 } 139 return c.JSON(results) 140 } 141 } 142 143 func (s TestkubeAPI) GetExecutorHandler() fiber.Handler { 144 return func(c *fiber.Ctx) error { 145 name := c.Params("name") 146 errPrefix := fmt.Sprintf("failed to get executor %s", name) 147 148 item, err := s.ExecutorsClient.Get(name) 149 if err != nil { 150 return s.Error(c, http.StatusBadGateway, fmt.Errorf("%s: client could not get executor: %w", errPrefix, err)) 151 } 152 153 if c.Accepts(mediaTypeJSON, mediaTypeYAML) == mediaTypeYAML { 154 result := executorsmapper.MapCRDToAPI(*item) 155 result.QuoteExecutorTextFields() 156 data, err := crd.GenerateYAML(crd.TemplateExecutor, []testkube.ExecutorUpsertRequest{result}) 157 return s.getCRDs(c, data, err) 158 } 159 160 result := executorsmapper.MapExecutorCRDToExecutorDetails(*item) 161 return c.JSON(result) 162 } 163 } 164 165 func (s TestkubeAPI) DeleteExecutorHandler() fiber.Handler { 166 return func(c *fiber.Ctx) error { 167 name := c.Params("name") 168 errPrefix := fmt.Sprintf("failed to delete executor %s", name) 169 170 err := s.ExecutorsClient.Delete(name) 171 if err != nil { 172 return s.Error(c, http.StatusBadGateway, fmt.Errorf("%s: client could not delete executor: %w", errPrefix, err)) 173 } 174 175 s.Events.Notify(testkube.NewEvent( 176 testkube.EventDeleted, 177 testkube.EventResourceExecutor, 178 name, 179 )) 180 181 c.Status(http.StatusNoContent) 182 183 return nil 184 } 185 } 186 187 func (s TestkubeAPI) DeleteExecutorsHandler() fiber.Handler { 188 return func(c *fiber.Ctx) error { 189 errPrefix := "failed to delete executors" 190 err := s.ExecutorsClient.DeleteByLabels(c.Query("selector")) 191 if err != nil { 192 return s.Error(c, http.StatusBadGateway, fmt.Errorf("%s: client could not delete executors: %w", errPrefix, err)) 193 } 194 195 c.Status(http.StatusNoContent) 196 return nil 197 } 198 } 199 200 func (s TestkubeAPI) GetExecutorByTestTypeHandler() fiber.Handler { 201 return func(c *fiber.Ctx) error { 202 errPrefix := "failed to get executor by test type" 203 204 testType := c.Query("testType", "") 205 if testType == "" { 206 return s.Error(c, http.StatusBadRequest, fmt.Errorf("%s: could not fine test type", errPrefix)) 207 } 208 209 item, err := s.ExecutorsClient.GetByType(testType) 210 if err != nil { 211 return s.Error(c, http.StatusBadGateway, fmt.Errorf("%s: client could not get executor: %w", errPrefix, err)) 212 } 213 214 if c.Accepts(mediaTypeJSON, mediaTypeYAML) == mediaTypeYAML { 215 result := executorsmapper.MapCRDToAPI(*item) 216 result.QuoteExecutorTextFields() 217 data, err := crd.GenerateYAML(crd.TemplateExecutor, []testkube.ExecutorUpsertRequest{result}) 218 return s.getCRDs(c, data, err) 219 } 220 221 result := executorsmapper.MapExecutorCRDToExecutorDetails(*item) 222 return c.JSON(result) 223 } 224 }