mirror of
https://github.com/redhat-developer/odo.git
synced 2025-10-19 03:06:19 +03:00
[ui] Edit container (#7077)
* [api] patch container * [ui] edit container * [ui] Initialize endpoint component * e2e tests * static ui files
This commit is contained in:
@@ -649,6 +649,126 @@ paths:
|
|||||||
example:
|
example:
|
||||||
message: "Error deleting the container"
|
message: "Error deleting the container"
|
||||||
|
|
||||||
|
patch:
|
||||||
|
tags:
|
||||||
|
- devstate
|
||||||
|
description: Update a container
|
||||||
|
parameters:
|
||||||
|
- name: containerName
|
||||||
|
in: path
|
||||||
|
description: Container name to update
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- image
|
||||||
|
properties:
|
||||||
|
image:
|
||||||
|
description: Container image
|
||||||
|
type: string
|
||||||
|
command:
|
||||||
|
description: Entrypoint of the container
|
||||||
|
type: array
|
||||||
|
items: {
|
||||||
|
type: string
|
||||||
|
}
|
||||||
|
args:
|
||||||
|
description: Args passed to the Container entrypoint
|
||||||
|
type: array
|
||||||
|
items: {
|
||||||
|
type: string
|
||||||
|
}
|
||||||
|
env:
|
||||||
|
description: Environment variables to define
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Env'
|
||||||
|
memReq:
|
||||||
|
description: Requested memory for the deployed container
|
||||||
|
type: string
|
||||||
|
memLimit:
|
||||||
|
description: Memory limit for the deployed container
|
||||||
|
type: string
|
||||||
|
cpuReq:
|
||||||
|
description: Requested CPU for the deployed container
|
||||||
|
type: string
|
||||||
|
cpuLimit:
|
||||||
|
description: CPU limit for the deployed container
|
||||||
|
type: string
|
||||||
|
volumeMounts:
|
||||||
|
description: Volume to mount into the container filesystem
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/VolumeMount'
|
||||||
|
configureSources:
|
||||||
|
description: If false, mountSources and sourceMapping values are not considered
|
||||||
|
type: boolean
|
||||||
|
mountSources:
|
||||||
|
description: If true, sources are mounted into container's filesystem
|
||||||
|
type: boolean
|
||||||
|
sourceMapping:
|
||||||
|
description: Specific directory on which to mount sources
|
||||||
|
type: string
|
||||||
|
annotation:
|
||||||
|
description: Annotations added to the resources created for this container
|
||||||
|
$ref: '#/components/schemas/Annotation'
|
||||||
|
endpoints:
|
||||||
|
description: Endpoints exposed by the container
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Endpoint'
|
||||||
|
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: container was successfully updated
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/DevfileContent'
|
||||||
|
example:
|
||||||
|
{
|
||||||
|
"content": "schemaVersion: 2.2.0\n",
|
||||||
|
"commands": [],
|
||||||
|
"containers": [],
|
||||||
|
"images": [],
|
||||||
|
"resources": [],
|
||||||
|
"events": {
|
||||||
|
"preStart": null,
|
||||||
|
"postStart": null,
|
||||||
|
"preStop": null,
|
||||||
|
"postStop": null
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"name": "",
|
||||||
|
"version": "",
|
||||||
|
"displayName": "",
|
||||||
|
description": "",
|
||||||
|
"tags": "",
|
||||||
|
"architectures": "",
|
||||||
|
"icon": "",
|
||||||
|
"globalMemoryLimit": "",
|
||||||
|
"projectType": "",
|
||||||
|
"language": "",
|
||||||
|
"website": "",
|
||||||
|
"provider": "",
|
||||||
|
"supportUrl": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'500':
|
||||||
|
description: Error updating the container
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/GeneralError'
|
||||||
|
example:
|
||||||
|
message: "Error updating the container"
|
||||||
|
|
||||||
/devstate/image:
|
/devstate/image:
|
||||||
post:
|
post:
|
||||||
tags:
|
tags:
|
||||||
|
|||||||
1
pkg/apiserver-gen/.openapi-generator/FILES
generated
1
pkg/apiserver-gen/.openapi-generator/FILES
generated
@@ -16,6 +16,7 @@ go/model__devstate_command__command_name__move_post_request.go
|
|||||||
go/model__devstate_command__command_name__set_default_post_request.go
|
go/model__devstate_command__command_name__set_default_post_request.go
|
||||||
go/model__devstate_composite_command__command_name__patch_request.go
|
go/model__devstate_composite_command__command_name__patch_request.go
|
||||||
go/model__devstate_composite_command_post_request.go
|
go/model__devstate_composite_command_post_request.go
|
||||||
|
go/model__devstate_container__container_name__patch_request.go
|
||||||
go/model__devstate_container_post_request.go
|
go/model__devstate_container_post_request.go
|
||||||
go/model__devstate_events_put_request.go
|
go/model__devstate_events_put_request.go
|
||||||
go/model__devstate_exec_command__command_name__patch_request.go
|
go/model__devstate_exec_command__command_name__patch_request.go
|
||||||
|
|||||||
2
pkg/apiserver-gen/go/api.go
generated
2
pkg/apiserver-gen/go/api.go
generated
@@ -41,6 +41,7 @@ type DevstateApiRouter interface {
|
|||||||
DevstateCompositeCommandCommandNamePatch(http.ResponseWriter, *http.Request)
|
DevstateCompositeCommandCommandNamePatch(http.ResponseWriter, *http.Request)
|
||||||
DevstateCompositeCommandPost(http.ResponseWriter, *http.Request)
|
DevstateCompositeCommandPost(http.ResponseWriter, *http.Request)
|
||||||
DevstateContainerContainerNameDelete(http.ResponseWriter, *http.Request)
|
DevstateContainerContainerNameDelete(http.ResponseWriter, *http.Request)
|
||||||
|
DevstateContainerContainerNamePatch(http.ResponseWriter, *http.Request)
|
||||||
DevstateContainerPost(http.ResponseWriter, *http.Request)
|
DevstateContainerPost(http.ResponseWriter, *http.Request)
|
||||||
DevstateDevfileDelete(http.ResponseWriter, *http.Request)
|
DevstateDevfileDelete(http.ResponseWriter, *http.Request)
|
||||||
DevstateDevfileGet(http.ResponseWriter, *http.Request)
|
DevstateDevfileGet(http.ResponseWriter, *http.Request)
|
||||||
@@ -90,6 +91,7 @@ type DevstateApiServicer interface {
|
|||||||
DevstateCompositeCommandCommandNamePatch(context.Context, string, DevstateCompositeCommandCommandNamePatchRequest) (ImplResponse, error)
|
DevstateCompositeCommandCommandNamePatch(context.Context, string, DevstateCompositeCommandCommandNamePatchRequest) (ImplResponse, error)
|
||||||
DevstateCompositeCommandPost(context.Context, DevstateCompositeCommandPostRequest) (ImplResponse, error)
|
DevstateCompositeCommandPost(context.Context, DevstateCompositeCommandPostRequest) (ImplResponse, error)
|
||||||
DevstateContainerContainerNameDelete(context.Context, string) (ImplResponse, error)
|
DevstateContainerContainerNameDelete(context.Context, string) (ImplResponse, error)
|
||||||
|
DevstateContainerContainerNamePatch(context.Context, string, DevstateContainerContainerNamePatchRequest) (ImplResponse, error)
|
||||||
DevstateContainerPost(context.Context, DevstateContainerPostRequest) (ImplResponse, error)
|
DevstateContainerPost(context.Context, DevstateContainerPostRequest) (ImplResponse, error)
|
||||||
DevstateDevfileDelete(context.Context) (ImplResponse, error)
|
DevstateDevfileDelete(context.Context) (ImplResponse, error)
|
||||||
DevstateDevfileGet(context.Context) (ImplResponse, error)
|
DevstateDevfileGet(context.Context) (ImplResponse, error)
|
||||||
|
|||||||
32
pkg/apiserver-gen/go/api_devstate.go
generated
32
pkg/apiserver-gen/go/api_devstate.go
generated
@@ -110,6 +110,12 @@ func (c *DevstateApiController) Routes() Routes {
|
|||||||
"/api/v1/devstate/container/{containerName}",
|
"/api/v1/devstate/container/{containerName}",
|
||||||
c.DevstateContainerContainerNameDelete,
|
c.DevstateContainerContainerNameDelete,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"DevstateContainerContainerNamePatch",
|
||||||
|
strings.ToUpper("Patch"),
|
||||||
|
"/api/v1/devstate/container/{containerName}",
|
||||||
|
c.DevstateContainerContainerNamePatch,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"DevstateContainerPost",
|
"DevstateContainerPost",
|
||||||
strings.ToUpper("Post"),
|
strings.ToUpper("Post"),
|
||||||
@@ -431,6 +437,32 @@ func (c *DevstateApiController) DevstateContainerContainerNameDelete(w http.Resp
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DevstateContainerContainerNamePatch -
|
||||||
|
func (c *DevstateApiController) DevstateContainerContainerNamePatch(w http.ResponseWriter, r *http.Request) {
|
||||||
|
params := mux.Vars(r)
|
||||||
|
containerNameParam := params["containerName"]
|
||||||
|
devstateContainerContainerNamePatchRequestParam := DevstateContainerContainerNamePatchRequest{}
|
||||||
|
d := json.NewDecoder(r.Body)
|
||||||
|
d.DisallowUnknownFields()
|
||||||
|
if err := d.Decode(&devstateContainerContainerNamePatchRequestParam); err != nil {
|
||||||
|
c.errorHandler(w, r, &ParsingError{Err: err}, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := AssertDevstateContainerContainerNamePatchRequestRequired(devstateContainerContainerNamePatchRequestParam); err != nil {
|
||||||
|
c.errorHandler(w, r, err, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
result, err := c.service.DevstateContainerContainerNamePatch(r.Context(), containerNameParam, devstateContainerContainerNamePatchRequestParam)
|
||||||
|
// If an error occurred, encode the error with the status code
|
||||||
|
if err != nil {
|
||||||
|
c.errorHandler(w, r, err, &result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// If no error, encode the body and the result code
|
||||||
|
EncodeJSONResponse(result.Body, &result.Code, w)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// DevstateContainerPost -
|
// DevstateContainerPost -
|
||||||
func (c *DevstateApiController) DevstateContainerPost(w http.ResponseWriter, r *http.Request) {
|
func (c *DevstateApiController) DevstateContainerPost(w http.ResponseWriter, r *http.Request) {
|
||||||
devstateContainerPostRequestParam := DevstateContainerPostRequest{}
|
devstateContainerPostRequestParam := DevstateContainerPostRequest{}
|
||||||
|
|||||||
98
pkg/apiserver-gen/go/model__devstate_container__container_name__patch_request.go
generated
Normal file
98
pkg/apiserver-gen/go/model__devstate_container__container_name__patch_request.go
generated
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* odo dev
|
||||||
|
*
|
||||||
|
* API interface for 'odo dev'
|
||||||
|
*
|
||||||
|
* API version: 0.1
|
||||||
|
* Generated by: OpenAPI Generator (https://openapi-generator.tech)
|
||||||
|
*/
|
||||||
|
|
||||||
|
package openapi
|
||||||
|
|
||||||
|
type DevstateContainerContainerNamePatchRequest struct {
|
||||||
|
|
||||||
|
// Container image
|
||||||
|
Image string `json:"image"`
|
||||||
|
|
||||||
|
// Entrypoint of the container
|
||||||
|
Command []string `json:"command,omitempty"`
|
||||||
|
|
||||||
|
// Args passed to the Container entrypoint
|
||||||
|
Args []string `json:"args,omitempty"`
|
||||||
|
|
||||||
|
// Environment variables to define
|
||||||
|
Env []Env `json:"env,omitempty"`
|
||||||
|
|
||||||
|
// Requested memory for the deployed container
|
||||||
|
MemReq string `json:"memReq,omitempty"`
|
||||||
|
|
||||||
|
// Memory limit for the deployed container
|
||||||
|
MemLimit string `json:"memLimit,omitempty"`
|
||||||
|
|
||||||
|
// Requested CPU for the deployed container
|
||||||
|
CpuReq string `json:"cpuReq,omitempty"`
|
||||||
|
|
||||||
|
// CPU limit for the deployed container
|
||||||
|
CpuLimit string `json:"cpuLimit,omitempty"`
|
||||||
|
|
||||||
|
// Volume to mount into the container filesystem
|
||||||
|
VolumeMounts []VolumeMount `json:"volumeMounts,omitempty"`
|
||||||
|
|
||||||
|
// If false, mountSources and sourceMapping values are not considered
|
||||||
|
ConfigureSources bool `json:"configureSources,omitempty"`
|
||||||
|
|
||||||
|
// If true, sources are mounted into container's filesystem
|
||||||
|
MountSources bool `json:"mountSources,omitempty"`
|
||||||
|
|
||||||
|
// Specific directory on which to mount sources
|
||||||
|
SourceMapping string `json:"sourceMapping,omitempty"`
|
||||||
|
|
||||||
|
Annotation Annotation `json:"annotation,omitempty"`
|
||||||
|
|
||||||
|
// Endpoints exposed by the container
|
||||||
|
Endpoints []Endpoint `json:"endpoints,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertDevstateContainerContainerNamePatchRequestRequired checks if the required fields are not zero-ed
|
||||||
|
func AssertDevstateContainerContainerNamePatchRequestRequired(obj DevstateContainerContainerNamePatchRequest) error {
|
||||||
|
elements := map[string]interface{}{
|
||||||
|
"image": obj.Image,
|
||||||
|
}
|
||||||
|
for name, el := range elements {
|
||||||
|
if isZero := IsZeroValue(el); isZero {
|
||||||
|
return &RequiredError{Field: name}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, el := range obj.Env {
|
||||||
|
if err := AssertEnvRequired(el); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, el := range obj.VolumeMounts {
|
||||||
|
if err := AssertVolumeMountRequired(el); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := AssertAnnotationRequired(obj.Annotation); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, el := range obj.Endpoints {
|
||||||
|
if err := AssertEndpointRequired(el); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertRecurseDevstateContainerContainerNamePatchRequestRequired recursively checks if required fields are not zero-ed in a nested slice.
|
||||||
|
// Accepts only nested slice of DevstateContainerContainerNamePatchRequest (e.g. [][]DevstateContainerContainerNamePatchRequest), otherwise ErrTypeAssertionError is thrown.
|
||||||
|
func AssertRecurseDevstateContainerContainerNamePatchRequestRequired(objSlice interface{}) error {
|
||||||
|
return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error {
|
||||||
|
aDevstateContainerContainerNamePatchRequest, ok := obj.(DevstateContainerContainerNamePatchRequest)
|
||||||
|
if !ok {
|
||||||
|
return ErrTypeAssertionError
|
||||||
|
}
|
||||||
|
return AssertDevstateContainerContainerNamePatchRequestRequired(aDevstateContainerContainerNamePatchRequest)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -384,7 +384,33 @@ func (s *DevstateApiService) DevstateCompositeCommandCommandNamePatch(ctx contex
|
|||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return openapi.Response(http.StatusInternalServerError, openapi.GeneralError{
|
return openapi.Response(http.StatusInternalServerError, openapi.GeneralError{
|
||||||
Message: fmt.Sprintf("Error updating the Image Command: %s", err),
|
Message: fmt.Sprintf("Error updating the Composite Command: %s", err),
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
return openapi.Response(http.StatusOK, newContent), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DevstateApiService) DevstateContainerContainerNamePatch(ctx context.Context, name string, patch openapi.DevstateContainerContainerNamePatchRequest) (openapi.ImplResponse, error) {
|
||||||
|
newContent, err := s.devfileState.PatchContainer(
|
||||||
|
name,
|
||||||
|
patch.Image,
|
||||||
|
patch.Command,
|
||||||
|
patch.Args,
|
||||||
|
patch.Env,
|
||||||
|
patch.MemReq,
|
||||||
|
patch.MemLimit,
|
||||||
|
patch.CpuReq,
|
||||||
|
patch.CpuLimit,
|
||||||
|
patch.VolumeMounts,
|
||||||
|
patch.ConfigureSources,
|
||||||
|
patch.MountSources,
|
||||||
|
patch.SourceMapping,
|
||||||
|
patch.Annotation,
|
||||||
|
patch.Endpoints,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return openapi.Response(http.StatusInternalServerError, openapi.GeneralError{
|
||||||
|
Message: fmt.Sprintf("Error updating the container: %s", err),
|
||||||
}), nil
|
}), nil
|
||||||
}
|
}
|
||||||
return openapi.Response(http.StatusOK, newContent), nil
|
return openapi.Response(http.StatusOK, newContent), nil
|
||||||
|
|||||||
@@ -27,44 +27,6 @@ func (o *DevfileState) AddContainer(
|
|||||||
annotation Annotation,
|
annotation Annotation,
|
||||||
endpoints []Endpoint,
|
endpoints []Endpoint,
|
||||||
) (DevfileContent, error) {
|
) (DevfileContent, error) {
|
||||||
v1alpha2VolumeMounts := make([]v1alpha2.VolumeMount, 0, len(volumeMounts))
|
|
||||||
for _, vm := range volumeMounts {
|
|
||||||
v1alpha2VolumeMounts = append(v1alpha2VolumeMounts, v1alpha2.VolumeMount{
|
|
||||||
Name: vm.Name,
|
|
||||||
Path: vm.Path,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
v1alpha2Envs := make([]v1alpha2.EnvVar, 0, len(envs))
|
|
||||||
for _, env := range envs {
|
|
||||||
v1alpha2Envs = append(v1alpha2Envs, v1alpha2.EnvVar{
|
|
||||||
Name: env.Name,
|
|
||||||
Value: env.Value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
var annotations *v1alpha2.Annotation
|
|
||||||
if len(annotation.Deployment) > 0 || len(annotation.Service) > 0 {
|
|
||||||
annotations = &v1alpha2.Annotation{}
|
|
||||||
if len(annotation.Deployment) > 0 {
|
|
||||||
annotations.Deployment = annotation.Deployment
|
|
||||||
}
|
|
||||||
if len(annotation.Service) > 0 {
|
|
||||||
annotations.Service = annotation.Service
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
v1alpha2Endpoints := make([]v1alpha2.Endpoint, 0, len(endpoints))
|
|
||||||
for _, endpoint := range endpoints {
|
|
||||||
endpoint := endpoint
|
|
||||||
v1alpha2Endpoints = append(v1alpha2Endpoints, v1alpha2.Endpoint{
|
|
||||||
Name: endpoint.Name,
|
|
||||||
TargetPort: int(endpoint.TargetPort),
|
|
||||||
Exposure: v1alpha2.EndpointExposure(endpoint.Exposure),
|
|
||||||
Protocol: v1alpha2.EndpointProtocol(endpoint.Protocol),
|
|
||||||
Secure: &endpoint.Secure,
|
|
||||||
Path: endpoint.Path,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
container := v1alpha2.Component{
|
container := v1alpha2.Component{
|
||||||
Name: name,
|
Name: name,
|
||||||
@@ -74,15 +36,15 @@ func (o *DevfileState) AddContainer(
|
|||||||
Image: image,
|
Image: image,
|
||||||
Command: command,
|
Command: command,
|
||||||
Args: args,
|
Args: args,
|
||||||
Env: v1alpha2Envs,
|
Env: tov1alpha2EnvVars(envs),
|
||||||
MemoryRequest: memRequest,
|
MemoryRequest: memRequest,
|
||||||
MemoryLimit: memLimit,
|
MemoryLimit: memLimit,
|
||||||
CpuRequest: cpuRequest,
|
CpuRequest: cpuRequest,
|
||||||
CpuLimit: cpuLimit,
|
CpuLimit: cpuLimit,
|
||||||
VolumeMounts: v1alpha2VolumeMounts,
|
VolumeMounts: tov1alpha2VolumeMounts(volumeMounts),
|
||||||
Annotation: annotations,
|
Annotation: tov1alpha2Annotation(annotation),
|
||||||
},
|
},
|
||||||
Endpoints: v1alpha2Endpoints,
|
Endpoints: tov1alpha2Endpoints(endpoints),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -97,6 +59,115 @@ func (o *DevfileState) AddContainer(
|
|||||||
return o.GetContent()
|
return o.GetContent()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *DevfileState) PatchContainer(
|
||||||
|
name string,
|
||||||
|
image string,
|
||||||
|
command []string,
|
||||||
|
args []string,
|
||||||
|
envs []Env,
|
||||||
|
memRequest string,
|
||||||
|
memLimit string,
|
||||||
|
cpuRequest string,
|
||||||
|
cpuLimit string,
|
||||||
|
volumeMounts []VolumeMount,
|
||||||
|
configureSources bool,
|
||||||
|
mountSources bool,
|
||||||
|
sourceMapping string,
|
||||||
|
annotation Annotation,
|
||||||
|
endpoints []Endpoint,
|
||||||
|
) (DevfileContent, error) {
|
||||||
|
found, err := o.Devfile.Data.GetComponents(common.DevfileOptions{
|
||||||
|
ComponentOptions: common.ComponentOptions{
|
||||||
|
ComponentType: v1alpha2.ContainerComponentType,
|
||||||
|
},
|
||||||
|
FilterByName: name,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return DevfileContent{}, err
|
||||||
|
}
|
||||||
|
if len(found) != 1 {
|
||||||
|
return DevfileContent{}, fmt.Errorf("%d Container found with name %q", len(found), name)
|
||||||
|
}
|
||||||
|
|
||||||
|
container := found[0]
|
||||||
|
container.Container.Image = image
|
||||||
|
container.Container.Command = command
|
||||||
|
container.Container.Args = args
|
||||||
|
container.Container.Env = tov1alpha2EnvVars(envs)
|
||||||
|
container.Container.MemoryRequest = memRequest
|
||||||
|
container.Container.MemoryLimit = memLimit
|
||||||
|
container.Container.CpuRequest = cpuRequest
|
||||||
|
container.Container.CpuLimit = cpuLimit
|
||||||
|
container.Container.VolumeMounts = tov1alpha2VolumeMounts(volumeMounts)
|
||||||
|
|
||||||
|
container.Container.MountSources = nil
|
||||||
|
container.Container.SourceMapping = ""
|
||||||
|
if configureSources {
|
||||||
|
container.Container.MountSources = &mountSources
|
||||||
|
container.Container.SourceMapping = sourceMapping
|
||||||
|
}
|
||||||
|
container.Container.Annotation = tov1alpha2Annotation(annotation)
|
||||||
|
container.Container.Endpoints = tov1alpha2Endpoints(endpoints)
|
||||||
|
|
||||||
|
err = o.Devfile.Data.UpdateComponent(container)
|
||||||
|
if err != nil {
|
||||||
|
return DevfileContent{}, err
|
||||||
|
}
|
||||||
|
return o.GetContent()
|
||||||
|
}
|
||||||
|
|
||||||
|
func tov1alpha2EnvVars(envs []Env) []v1alpha2.EnvVar {
|
||||||
|
result := make([]v1alpha2.EnvVar, 0, len(envs))
|
||||||
|
for _, env := range envs {
|
||||||
|
result = append(result, v1alpha2.EnvVar{
|
||||||
|
Name: env.Name,
|
||||||
|
Value: env.Value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func tov1alpha2VolumeMounts(volumeMounts []VolumeMount) []v1alpha2.VolumeMount {
|
||||||
|
result := make([]v1alpha2.VolumeMount, 0, len(volumeMounts))
|
||||||
|
for _, vm := range volumeMounts {
|
||||||
|
result = append(result, v1alpha2.VolumeMount{
|
||||||
|
Name: vm.Name,
|
||||||
|
Path: vm.Path,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func tov1alpha2Annotation(annotation Annotation) *v1alpha2.Annotation {
|
||||||
|
var result *v1alpha2.Annotation
|
||||||
|
if len(annotation.Deployment) > 0 || len(annotation.Service) > 0 {
|
||||||
|
result = &v1alpha2.Annotation{}
|
||||||
|
if len(annotation.Deployment) > 0 {
|
||||||
|
result.Deployment = annotation.Deployment
|
||||||
|
}
|
||||||
|
if len(annotation.Service) > 0 {
|
||||||
|
result.Service = annotation.Service
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func tov1alpha2Endpoints(endpoints []Endpoint) []v1alpha2.Endpoint {
|
||||||
|
result := make([]v1alpha2.Endpoint, 0, len(endpoints))
|
||||||
|
for _, endpoint := range endpoints {
|
||||||
|
endpoint := endpoint
|
||||||
|
result = append(result, v1alpha2.Endpoint{
|
||||||
|
Name: endpoint.Name,
|
||||||
|
TargetPort: int(endpoint.TargetPort),
|
||||||
|
Exposure: v1alpha2.EndpointExposure(endpoint.Exposure),
|
||||||
|
Protocol: v1alpha2.EndpointProtocol(endpoint.Protocol),
|
||||||
|
Secure: &endpoint.Secure,
|
||||||
|
Path: endpoint.Path,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
func (o *DevfileState) DeleteContainer(name string) (DevfileContent, error) {
|
func (o *DevfileState) DeleteContainer(name string) (DevfileContent, error) {
|
||||||
|
|
||||||
err := o.checkContainerUsed(name)
|
err := o.checkContainerUsed(name)
|
||||||
|
|||||||
@@ -649,6 +649,126 @@ paths:
|
|||||||
example:
|
example:
|
||||||
message: "Error deleting the container"
|
message: "Error deleting the container"
|
||||||
|
|
||||||
|
patch:
|
||||||
|
tags:
|
||||||
|
- devstate
|
||||||
|
description: Update a container
|
||||||
|
parameters:
|
||||||
|
- name: containerName
|
||||||
|
in: path
|
||||||
|
description: Container name to update
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- image
|
||||||
|
properties:
|
||||||
|
image:
|
||||||
|
description: Container image
|
||||||
|
type: string
|
||||||
|
command:
|
||||||
|
description: Entrypoint of the container
|
||||||
|
type: array
|
||||||
|
items: {
|
||||||
|
type: string
|
||||||
|
}
|
||||||
|
args:
|
||||||
|
description: Args passed to the Container entrypoint
|
||||||
|
type: array
|
||||||
|
items: {
|
||||||
|
type: string
|
||||||
|
}
|
||||||
|
env:
|
||||||
|
description: Environment variables to define
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Env'
|
||||||
|
memReq:
|
||||||
|
description: Requested memory for the deployed container
|
||||||
|
type: string
|
||||||
|
memLimit:
|
||||||
|
description: Memory limit for the deployed container
|
||||||
|
type: string
|
||||||
|
cpuReq:
|
||||||
|
description: Requested CPU for the deployed container
|
||||||
|
type: string
|
||||||
|
cpuLimit:
|
||||||
|
description: CPU limit for the deployed container
|
||||||
|
type: string
|
||||||
|
volumeMounts:
|
||||||
|
description: Volume to mount into the container filesystem
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/VolumeMount'
|
||||||
|
configureSources:
|
||||||
|
description: If false, mountSources and sourceMapping values are not considered
|
||||||
|
type: boolean
|
||||||
|
mountSources:
|
||||||
|
description: If true, sources are mounted into container's filesystem
|
||||||
|
type: boolean
|
||||||
|
sourceMapping:
|
||||||
|
description: Specific directory on which to mount sources
|
||||||
|
type: string
|
||||||
|
annotation:
|
||||||
|
description: Annotations added to the resources created for this container
|
||||||
|
$ref: '#/components/schemas/Annotation'
|
||||||
|
endpoints:
|
||||||
|
description: Endpoints exposed by the container
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Endpoint'
|
||||||
|
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: container was successfully updated
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/DevfileContent'
|
||||||
|
example:
|
||||||
|
{
|
||||||
|
"content": "schemaVersion: 2.2.0\n",
|
||||||
|
"commands": [],
|
||||||
|
"containers": [],
|
||||||
|
"images": [],
|
||||||
|
"resources": [],
|
||||||
|
"events": {
|
||||||
|
"preStart": null,
|
||||||
|
"postStart": null,
|
||||||
|
"preStop": null,
|
||||||
|
"postStop": null
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"name": "",
|
||||||
|
"version": "",
|
||||||
|
"displayName": "",
|
||||||
|
description": "",
|
||||||
|
"tags": "",
|
||||||
|
"architectures": "",
|
||||||
|
"icon": "",
|
||||||
|
"globalMemoryLimit": "",
|
||||||
|
"projectType": "",
|
||||||
|
"language": "",
|
||||||
|
"website": "",
|
||||||
|
"provider": "",
|
||||||
|
"supportUrl": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'500':
|
||||||
|
description: Error updating the container
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/GeneralError'
|
||||||
|
example:
|
||||||
|
message: "Error updating the container"
|
||||||
|
|
||||||
/devstate/image:
|
/devstate/image:
|
||||||
post:
|
post:
|
||||||
tags:
|
tags:
|
||||||
|
|||||||
2
pkg/apiserver-impl/ui/index.html
generated
2
pkg/apiserver-impl/ui/index.html
generated
@@ -11,6 +11,6 @@
|
|||||||
<body class="mat-typography">
|
<body class="mat-typography">
|
||||||
<div id="loading">Loading, please wait...</div>
|
<div id="loading">Loading, please wait...</div>
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
<script src="runtime.1289ea0acffcdc5e.js" type="module"></script><script src="polyfills.8b3b37cedaf377c3.js" type="module"></script><script src="main.30e0dd4eb9375796.js" type="module"></script>
|
<script src="runtime.1289ea0acffcdc5e.js" type="module"></script><script src="polyfills.8b3b37cedaf377c3.js" type="module"></script><script src="main.d81b67faa7ca223c.js" type="module"></script>
|
||||||
|
|
||||||
</body></html>
|
</body></html>
|
||||||
File diff suppressed because one or more lines are too long
@@ -116,6 +116,118 @@ describe('devfile editor spec', () => {
|
|||||||
.should('contain.text', 'volume2');
|
.should('contain.text', 'volume2');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it.only('displays a modified container', () => {
|
||||||
|
cy.init();
|
||||||
|
|
||||||
|
cy.selectTab(TAB_VOLUMES);
|
||||||
|
cy.getByDataCy('volume-name').type('volume1');
|
||||||
|
cy.getByDataCy('volume-size').type('512Mi');
|
||||||
|
cy.getByDataCy('volume-ephemeral').click();
|
||||||
|
cy.getByDataCy('volume-create').click();
|
||||||
|
|
||||||
|
cy.selectTab(TAB_CONTAINERS);
|
||||||
|
cy.getByDataCy('container-name').type('created-container');
|
||||||
|
cy.getByDataCy('container-image').type('an-image');
|
||||||
|
cy.getByDataCy('container-env-add').click();
|
||||||
|
cy.getByDataCy('container-env-name-0').type("VAR1");
|
||||||
|
cy.getByDataCy('container-env-value-0').type("val1");
|
||||||
|
cy.getByDataCy('container-env-plus').click();
|
||||||
|
cy.getByDataCy('container-env-name-1').type("VAR2");
|
||||||
|
cy.getByDataCy('container-env-value-1').type("val2");
|
||||||
|
cy.getByDataCy('container-env-plus').click();
|
||||||
|
cy.getByDataCy('container-env-name-2').type("VAR3");
|
||||||
|
cy.getByDataCy('container-env-value-2').type("val3");
|
||||||
|
|
||||||
|
cy.getByDataCy('volume-mount-add').click();
|
||||||
|
cy.getByDataCy('volume-mount-path-0').type("/mnt/vol1", {force: true});
|
||||||
|
cy.getByDataCy('volume-mount-name-0').click().get('mat-option').contains('volume1').click();
|
||||||
|
|
||||||
|
cy.getByDataCy('endpoints-add').click();
|
||||||
|
cy.getByDataCy('endpoint-name-0').type("ep1");
|
||||||
|
cy.getByDataCy('endpoint-targetPort-0').type("4001");
|
||||||
|
|
||||||
|
cy.getByDataCy('volume-mount-add').click();
|
||||||
|
cy.getByDataCy('volume-mount-path-1').type("/mnt/vol2", {force: true});
|
||||||
|
cy.getByDataCy('volume-mount-name-1').click().get('mat-option').contains('(New Volume)').click();
|
||||||
|
cy.getByDataCy('volume-name').type('volume2');
|
||||||
|
cy.getByDataCy('volume-create').click();
|
||||||
|
|
||||||
|
cy.getByDataCy('container-more-params').click();
|
||||||
|
cy.getByDataCy('container-deploy-anno-add').click();
|
||||||
|
cy.getByDataCy('container-deploy-anno-name-0').type("DEPANNO1");
|
||||||
|
cy.getByDataCy('container-deploy-anno-value-0').type("depval1");
|
||||||
|
cy.getByDataCy('container-deploy-anno-plus').click();
|
||||||
|
cy.getByDataCy('container-deploy-anno-name-1').type("DEPANNO2");
|
||||||
|
cy.getByDataCy('container-deploy-anno-value-1').type("depval2");
|
||||||
|
cy.getByDataCy('container-svc-anno-add').click();
|
||||||
|
cy.getByDataCy('container-svc-anno-name-0').type("SVCANNO1");
|
||||||
|
cy.getByDataCy('container-svc-anno-value-0').type("svcval1");
|
||||||
|
cy.getByDataCy('container-svc-anno-plus').click();
|
||||||
|
cy.getByDataCy('container-svc-anno-name-1').type("SVCANNO2");
|
||||||
|
cy.getByDataCy('container-svc-anno-value-1').type("svcval2");
|
||||||
|
|
||||||
|
cy.getByDataCy('container-create').click();
|
||||||
|
|
||||||
|
cy.getByDataCy('container-edit').click();
|
||||||
|
|
||||||
|
cy.getByDataCy('container-image').type('{selectAll}{del}another-image');
|
||||||
|
cy.getByDataCy('container-env-plus').click();
|
||||||
|
cy.getByDataCy('container-env-name-3').type("VAR4");
|
||||||
|
cy.getByDataCy('container-env-value-3').type("val4");
|
||||||
|
|
||||||
|
cy.getByDataCy('volume-mount-path-0').type("{selectAll}{del}/mnt/other/vol1", {force: true});
|
||||||
|
cy.getByDataCy('volume-mount-name-0').click().get('mat-option').contains('volume1').click();
|
||||||
|
|
||||||
|
cy.getByDataCy('endpoint-targetPort-0').type("{selectAll}{del}4002");
|
||||||
|
|
||||||
|
cy.getByDataCy('volume-mount-add').click();
|
||||||
|
cy.getByDataCy('volume-mount-path-2').type("/mnt/vol3", {force: true});
|
||||||
|
cy.getByDataCy('volume-mount-name-2').click().get('mat-option').contains('(New Volume)').click();
|
||||||
|
cy.getByDataCy('volume-name').type('volume3');
|
||||||
|
cy.getByDataCy('volume-create').click();
|
||||||
|
|
||||||
|
cy.getByDataCy('container-more-params').click();
|
||||||
|
cy.getByDataCy('container-deploy-anno-name-0').type("{selectAll}{del}DEPANNO1b");
|
||||||
|
cy.getByDataCy('container-deploy-anno-value-0').type("{selectAll}{del}depval1b");
|
||||||
|
cy.getByDataCy('container-deploy-anno-plus').click();
|
||||||
|
cy.getByDataCy('container-deploy-anno-name-2').type("DEPANNO3");
|
||||||
|
cy.getByDataCy('container-deploy-anno-value-2').type("depval3");
|
||||||
|
cy.getByDataCy('container-svc-anno-name-0').type("{selectAll}{del}SVCANNO1b");
|
||||||
|
cy.getByDataCy('container-svc-anno-value-0').type("{selectAll}{del}svcval1b");
|
||||||
|
cy.getByDataCy('container-svc-anno-plus').click();
|
||||||
|
cy.getByDataCy('container-svc-anno-name-2').type("SVCANNO3");
|
||||||
|
cy.getByDataCy('container-svc-anno-value-2').type("svcval3");
|
||||||
|
|
||||||
|
cy.getByDataCy('container-save').click();
|
||||||
|
|
||||||
|
cy.getByDataCy('container-info').first()
|
||||||
|
.should('contain.text', 'another-image')
|
||||||
|
.should('contain.text', 'VAR1: val1')
|
||||||
|
.should('contain.text', 'VAR2: val2')
|
||||||
|
.should('contain.text', 'VAR3: val3')
|
||||||
|
.should('contain.text', 'VAR4: val4')
|
||||||
|
.should('contain.text', 'volume1')
|
||||||
|
.should('contain.text', '/mnt/other/vol1')
|
||||||
|
.should('contain.text', 'volume2')
|
||||||
|
.should('contain.text', '/mnt/vol2')
|
||||||
|
.should('not.contain.text', 'Mount Sources')
|
||||||
|
.should('contain.text', 'ep1')
|
||||||
|
.should('contain.text', '4002')
|
||||||
|
.should('contain.text', 'Deployment Annotations')
|
||||||
|
.should('contain.text', 'DEPANNO1b: depval1b')
|
||||||
|
.should('contain.text', 'DEPANNO2: depval2')
|
||||||
|
.should('contain.text', 'DEPANNO3: depval3')
|
||||||
|
.should('contain.text', 'Service Annotations')
|
||||||
|
.should('contain.text', 'SVCANNO1b: svcval1b')
|
||||||
|
.should('contain.text', 'SVCANNO2: svcval2')
|
||||||
|
.should('contain.text', 'SVCANNO3: svcval3');
|
||||||
|
|
||||||
|
cy.selectTab(TAB_VOLUMES);
|
||||||
|
cy.getByDataCy('volume-info').eq(1)
|
||||||
|
.should('contain.text', 'volume2');
|
||||||
|
});
|
||||||
|
|
||||||
it('displays a created container with source configuration', () => {
|
it('displays a created container with source configuration', () => {
|
||||||
cy.init();
|
cy.init();
|
||||||
|
|
||||||
|
|||||||
1
ui/src/app/api-gen/.openapi-generator/FILES
generated
1
ui/src/app/api-gen/.openapi-generator/FILES
generated
@@ -25,6 +25,7 @@ model/devstateCommandCommandNameMovePostRequest.ts
|
|||||||
model/devstateCommandCommandNameSetDefaultPostRequest.ts
|
model/devstateCommandCommandNameSetDefaultPostRequest.ts
|
||||||
model/devstateCompositeCommandCommandNamePatchRequest.ts
|
model/devstateCompositeCommandCommandNamePatchRequest.ts
|
||||||
model/devstateCompositeCommandPostRequest.ts
|
model/devstateCompositeCommandPostRequest.ts
|
||||||
|
model/devstateContainerContainerNamePatchRequest.ts
|
||||||
model/devstateContainerPostRequest.ts
|
model/devstateContainerPostRequest.ts
|
||||||
model/devstateDevfilePutRequest.ts
|
model/devstateDevfilePutRequest.ts
|
||||||
model/devstateEventsPutRequest.ts
|
model/devstateEventsPutRequest.ts
|
||||||
|
|||||||
71
ui/src/app/api-gen/api/devstate.service.ts
generated
71
ui/src/app/api-gen/api/devstate.service.ts
generated
@@ -35,6 +35,8 @@ import { DevstateCompositeCommandCommandNamePatchRequest } from '../model/devsta
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { DevstateCompositeCommandPostRequest } from '../model/devstateCompositeCommandPostRequest';
|
import { DevstateCompositeCommandPostRequest } from '../model/devstateCompositeCommandPostRequest';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
import { DevstateContainerContainerNamePatchRequest } from '../model/devstateContainerContainerNamePatchRequest';
|
||||||
|
// @ts-ignore
|
||||||
import { DevstateContainerPostRequest } from '../model/devstateContainerPostRequest';
|
import { DevstateContainerPostRequest } from '../model/devstateContainerPostRequest';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { DevstateDevfilePutRequest } from '../model/devstateDevfilePutRequest';
|
import { DevstateDevfilePutRequest } from '../model/devstateDevfilePutRequest';
|
||||||
@@ -769,6 +771,75 @@ export class DevstateService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a container
|
||||||
|
* @param containerName Container name to update
|
||||||
|
* @param devstateContainerContainerNamePatchRequest
|
||||||
|
* @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
|
||||||
|
* @param reportProgress flag to report request and response progress.
|
||||||
|
*/
|
||||||
|
public devstateContainerContainerNamePatch(containerName: string, devstateContainerContainerNamePatchRequest?: DevstateContainerContainerNamePatchRequest, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<DevfileContent>;
|
||||||
|
public devstateContainerContainerNamePatch(containerName: string, devstateContainerContainerNamePatchRequest?: DevstateContainerContainerNamePatchRequest, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpResponse<DevfileContent>>;
|
||||||
|
public devstateContainerContainerNamePatch(containerName: string, devstateContainerContainerNamePatchRequest?: DevstateContainerContainerNamePatchRequest, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<HttpEvent<DevfileContent>>;
|
||||||
|
public devstateContainerContainerNamePatch(containerName: string, devstateContainerContainerNamePatchRequest?: DevstateContainerContainerNamePatchRequest, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable<any> {
|
||||||
|
if (containerName === null || containerName === undefined) {
|
||||||
|
throw new Error('Required parameter containerName was null or undefined when calling devstateContainerContainerNamePatch.');
|
||||||
|
}
|
||||||
|
|
||||||
|
let localVarHeaders = this.defaultHeaders;
|
||||||
|
|
||||||
|
let localVarHttpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept;
|
||||||
|
if (localVarHttpHeaderAcceptSelected === undefined) {
|
||||||
|
// to determine the Accept header
|
||||||
|
const httpHeaderAccepts: string[] = [
|
||||||
|
'application/json'
|
||||||
|
];
|
||||||
|
localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts);
|
||||||
|
}
|
||||||
|
if (localVarHttpHeaderAcceptSelected !== undefined) {
|
||||||
|
localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
|
||||||
|
}
|
||||||
|
|
||||||
|
let localVarHttpContext: HttpContext | undefined = options && options.context;
|
||||||
|
if (localVarHttpContext === undefined) {
|
||||||
|
localVarHttpContext = new HttpContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// to determine the Content-Type header
|
||||||
|
const consumes: string[] = [
|
||||||
|
'application/json'
|
||||||
|
];
|
||||||
|
const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes);
|
||||||
|
if (httpContentTypeSelected !== undefined) {
|
||||||
|
localVarHeaders = localVarHeaders.set('Content-Type', httpContentTypeSelected);
|
||||||
|
}
|
||||||
|
|
||||||
|
let responseType_: 'text' | 'json' | 'blob' = 'json';
|
||||||
|
if (localVarHttpHeaderAcceptSelected) {
|
||||||
|
if (localVarHttpHeaderAcceptSelected.startsWith('text')) {
|
||||||
|
responseType_ = 'text';
|
||||||
|
} else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) {
|
||||||
|
responseType_ = 'json';
|
||||||
|
} else {
|
||||||
|
responseType_ = 'blob';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let localVarPath = `/devstate/container/${this.configuration.encodeParam({name: "containerName", value: containerName, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: undefined})}`;
|
||||||
|
return this.httpClient.request<DevfileContent>('patch', `${this.configuration.basePath}${localVarPath}`,
|
||||||
|
{
|
||||||
|
context: localVarHttpContext,
|
||||||
|
body: devstateContainerContainerNamePatchRequest,
|
||||||
|
responseType: <any>responseType_,
|
||||||
|
withCredentials: this.configuration.withCredentials,
|
||||||
|
headers: localVarHeaders,
|
||||||
|
observe: observe,
|
||||||
|
reportProgress: reportProgress
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new container to the Devfile
|
* Add a new container to the Devfile
|
||||||
* @param devstateContainerPostRequest
|
* @param devstateContainerPostRequest
|
||||||
|
|||||||
73
ui/src/app/api-gen/model/devstateContainerContainerNamePatchRequest.ts
generated
Normal file
73
ui/src/app/api-gen/model/devstateContainerContainerNamePatchRequest.ts
generated
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/**
|
||||||
|
* odo dev
|
||||||
|
* API interface for \'odo dev\'
|
||||||
|
*
|
||||||
|
* The version of the OpenAPI document: 0.1
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
* https://openapi-generator.tech
|
||||||
|
* Do not edit the class manually.
|
||||||
|
*/
|
||||||
|
import { Endpoint } from './endpoint';
|
||||||
|
import { VolumeMount } from './volumeMount';
|
||||||
|
import { Env } from './env';
|
||||||
|
import { Annotation } from './annotation';
|
||||||
|
|
||||||
|
|
||||||
|
export interface DevstateContainerContainerNamePatchRequest {
|
||||||
|
/**
|
||||||
|
* Container image
|
||||||
|
*/
|
||||||
|
image: string;
|
||||||
|
/**
|
||||||
|
* Entrypoint of the container
|
||||||
|
*/
|
||||||
|
command?: Array<string>;
|
||||||
|
/**
|
||||||
|
* Args passed to the Container entrypoint
|
||||||
|
*/
|
||||||
|
args?: Array<string>;
|
||||||
|
/**
|
||||||
|
* Environment variables to define
|
||||||
|
*/
|
||||||
|
env?: Array<Env>;
|
||||||
|
/**
|
||||||
|
* Requested memory for the deployed container
|
||||||
|
*/
|
||||||
|
memReq?: string;
|
||||||
|
/**
|
||||||
|
* Memory limit for the deployed container
|
||||||
|
*/
|
||||||
|
memLimit?: string;
|
||||||
|
/**
|
||||||
|
* Requested CPU for the deployed container
|
||||||
|
*/
|
||||||
|
cpuReq?: string;
|
||||||
|
/**
|
||||||
|
* CPU limit for the deployed container
|
||||||
|
*/
|
||||||
|
cpuLimit?: string;
|
||||||
|
/**
|
||||||
|
* Volume to mount into the container filesystem
|
||||||
|
*/
|
||||||
|
volumeMounts?: Array<VolumeMount>;
|
||||||
|
/**
|
||||||
|
* If false, mountSources and sourceMapping values are not considered
|
||||||
|
*/
|
||||||
|
configureSources?: boolean;
|
||||||
|
/**
|
||||||
|
* If true, sources are mounted into container\'s filesystem
|
||||||
|
*/
|
||||||
|
mountSources?: boolean;
|
||||||
|
/**
|
||||||
|
* Specific directory on which to mount sources
|
||||||
|
*/
|
||||||
|
sourceMapping?: string;
|
||||||
|
annotation?: Annotation;
|
||||||
|
/**
|
||||||
|
* Endpoints exposed by the container
|
||||||
|
*/
|
||||||
|
endpoints?: Array<Endpoint>;
|
||||||
|
}
|
||||||
|
|
||||||
1
ui/src/app/api-gen/model/models.ts
generated
1
ui/src/app/api-gen/model/models.ts
generated
@@ -15,6 +15,7 @@ export * from './devstateCommandCommandNameMovePostRequest';
|
|||||||
export * from './devstateCommandCommandNameSetDefaultPostRequest';
|
export * from './devstateCommandCommandNameSetDefaultPostRequest';
|
||||||
export * from './devstateCompositeCommandCommandNamePatchRequest';
|
export * from './devstateCompositeCommandCommandNamePatchRequest';
|
||||||
export * from './devstateCompositeCommandPostRequest';
|
export * from './devstateCompositeCommandPostRequest';
|
||||||
|
export * from './devstateContainerContainerNamePatchRequest';
|
||||||
export * from './devstateContainerPostRequest';
|
export * from './devstateContainerPostRequest';
|
||||||
export * from './devstateDevfilePutRequest';
|
export * from './devstateDevfilePutRequest';
|
||||||
export * from './devstateEventsPutRequest';
|
export * from './devstateEventsPutRequest';
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import { Component, forwardRef } from '@angular/core';
|
import { Component, forwardRef } from '@angular/core';
|
||||||
import { AbstractControl, ControlValueAccessor, FormArray, FormControl, FormGroup, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, Validator, Validators } from '@angular/forms';
|
import { AbstractControl, ControlValueAccessor, FormArray, FormControl, FormGroup, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, Validator, Validators } from '@angular/forms';
|
||||||
|
import { Endpoint } from 'src/app/api-gen';
|
||||||
interface Endpoint {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-endpoints',
|
selector: 'app-endpoints',
|
||||||
@@ -35,23 +32,29 @@ export class EndpointsComponent implements ControlValueAccessor, Validator {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
newEndpoint(): FormGroup {
|
newEndpoint(ep: Endpoint): FormGroup {
|
||||||
return new FormGroup({
|
return new FormGroup({
|
||||||
name: new FormControl("", [Validators.required]),
|
name: new FormControl(ep.name, [Validators.required]),
|
||||||
targetPort: new FormControl("", [Validators.required, Validators.pattern("^[0-9]*$")]),
|
targetPort: new FormControl(ep.targetPort, [Validators.required, Validators.pattern("^[0-9]*$")]),
|
||||||
exposure: new FormControl(""),
|
exposure: new FormControl(ep.exposure),
|
||||||
path: new FormControl(""),
|
path: new FormControl(ep.path),
|
||||||
protocol: new FormControl(""),
|
protocol: new FormControl(ep.protocol),
|
||||||
secure: new FormControl(false),
|
secure: new FormControl(ep.secure),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addEndpoint() {
|
addEndpoint() {
|
||||||
this.form.push(this.newEndpoint());
|
this.form.push(this.newEndpoint({
|
||||||
|
name: '',
|
||||||
|
targetPort: 0,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ControlValueAccessor implementation */
|
/* ControlValueAccessor implementation */
|
||||||
writeValue(value: Endpoint[]) {
|
writeValue(value: Endpoint[]) {
|
||||||
|
value.forEach(ep => {
|
||||||
|
this.form.push(this.newEndpoint(ep));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
registerOnChange(onChange: any) {
|
registerOnChange(onChange: any) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<div class="main">
|
<div class="main">
|
||||||
<h2>Add a new container</h2>
|
<h2 *ngIf="!container">Add a new container</h2>
|
||||||
|
<h2 *ngIf="container">Edit container <i>{{container.name}}</i></h2>
|
||||||
<div class="description">A Container is used to execute shell commands into a specific environment. The entrypoint of the container must be a non-terminating command. You can use an image pulled from a registry or an image built by an Image command.</div>
|
<div class="description">A Container is used to execute shell commands into a specific environment. The entrypoint of the container must be a non-terminating command. You can use an image pulled from a registry or an image built by an Image command.</div>
|
||||||
<form [formGroup]="form">
|
<form [formGroup]="form">
|
||||||
<mat-form-field appearance="outline" class="mid-width">
|
<mat-form-field appearance="outline" class="mid-width">
|
||||||
@@ -86,7 +87,8 @@
|
|||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="buttonbar">
|
<div class="buttonbar">
|
||||||
<button data-cy="container-create" [disabled]="form.invalid" mat-flat-button color="primary" matTooltip="create new container" (click)="create()">Create</button>
|
<button *ngIf="!container" data-cy="container-create" [disabled]="form.invalid" mat-flat-button color="primary" matTooltip="create new container" (click)="create()">Create</button>
|
||||||
|
<button *ngIf="container" data-cy="container-save" [disabled]="form.invalid" mat-flat-button color="primary" matTooltip="save container" (click)="save()">Save</button>
|
||||||
<button *ngIf="cancelable" mat-flat-button (click)="cancel()">Cancel</button>
|
<button *ngIf="cancelable" mat-flat-button (click)="cancel()">Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
import { Component, EventEmitter, Input, Output, SimpleChanges } from '@angular/core';
|
||||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||||
import { PATTERN_COMPONENT_ID } from '../patterns';
|
import { PATTERN_COMPONENT_ID } from '../patterns';
|
||||||
import { DevstateService } from 'src/app/services/devstate.service';
|
import { DevstateService } from 'src/app/services/devstate.service';
|
||||||
@@ -18,8 +18,11 @@ export interface ToCreate {
|
|||||||
export class ContainerComponent {
|
export class ContainerComponent {
|
||||||
@Input() volumeNames: string[] = [];
|
@Input() volumeNames: string[] = [];
|
||||||
@Input() cancelable: boolean = false;
|
@Input() cancelable: boolean = false;
|
||||||
|
@Input() container: Container | undefined;
|
||||||
|
|
||||||
@Output() canceled = new EventEmitter<void>();
|
@Output() canceled = new EventEmitter<void>();
|
||||||
@Output() created = new EventEmitter<ToCreate>();
|
@Output() created = new EventEmitter<ToCreate>();
|
||||||
|
@Output() saved = new EventEmitter<ToCreate>();
|
||||||
|
|
||||||
form: FormGroup;
|
form: FormGroup;
|
||||||
|
|
||||||
@@ -79,17 +82,27 @@ export class ContainerComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toObject(o: {name: string, value: string}[]) {
|
||||||
|
if (o == null) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return o.reduce((acc: any, val: {name: string, value: string}) => { acc[val.name] = val.value; return acc; }, {});
|
||||||
|
};
|
||||||
|
|
||||||
|
fromObject(o: any) {
|
||||||
|
if (o == null) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return Object.keys(o).map(k => ({ name: k, value: o[k]}));
|
||||||
|
}
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
this.telemetry.track("[ui] create container");
|
this.telemetry.track("[ui] create container");
|
||||||
|
|
||||||
const toObject = (o: {name: string, value: string}[]) => {
|
|
||||||
return o.reduce((acc: any, val: {name: string, value: string}) => { acc[val.name] = val.value; return acc; }, {});
|
|
||||||
};
|
|
||||||
|
|
||||||
const container = this.form.value;
|
const container = this.form.value;
|
||||||
container.annotation = {
|
container.annotation = {
|
||||||
deployment: toObject(container.deployAnnotations),
|
deployment: this.toObject(container.deployAnnotations),
|
||||||
service: toObject(container.svcAnnotations),
|
service: this.toObject(container.svcAnnotations),
|
||||||
};
|
};
|
||||||
this.created.emit({
|
this.created.emit({
|
||||||
container: this.form.value,
|
container: this.form.value,
|
||||||
@@ -97,10 +110,43 @@ export class ContainerComponent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
save() {
|
||||||
|
this.telemetry.track("[ui] edit container");
|
||||||
|
const newValue = this.form.value;
|
||||||
|
newValue.name = this.container?.name;
|
||||||
|
newValue.annotation = {
|
||||||
|
deployment: this.toObject(newValue.deployAnnotations),
|
||||||
|
service: this.toObject(newValue.svcAnnotations),
|
||||||
|
};
|
||||||
|
this.saved.emit({
|
||||||
|
container: newValue,
|
||||||
|
volumes: this.volumesToCreate,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
cancel() {
|
cancel() {
|
||||||
this.canceled.emit();
|
this.canceled.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
|
if (!changes['container']) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const container = changes['container'].currentValue;
|
||||||
|
if (container == undefined) {
|
||||||
|
this.form.get('name')?.enable();
|
||||||
|
} else {
|
||||||
|
this.form.reset();
|
||||||
|
this.form.patchValue(container);
|
||||||
|
this.form.get('name')?.disable();
|
||||||
|
if (this.form.get('sourceMapping')?.value != '') {
|
||||||
|
this.form.get('_specificDir')?.setValue(true);
|
||||||
|
}
|
||||||
|
this.form.get('deployAnnotations')?.setValue(this.fromObject(container.annotation.deployment));
|
||||||
|
this.form.get('svcAnnotations')?.setValue(this.fromObject(container.annotation.service));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onCreateNewVolume(v: Volume) {
|
onCreateNewVolume(v: Volume) {
|
||||||
this.volumesToCreate.push(v);
|
this.volumesToCreate.push(v);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,28 @@ export class DevstateService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
saveContainer(container: Container): Observable<DevfileContent> {
|
||||||
|
return this.http.patch<DevfileContent>(this.base+"/container/"+container.name, {
|
||||||
|
image: container.image,
|
||||||
|
command: container.command,
|
||||||
|
args: container.args,
|
||||||
|
env: container.env,
|
||||||
|
memReq: container.memoryRequest,
|
||||||
|
memLimit: container.memoryLimit,
|
||||||
|
cpuReq: container.cpuRequest,
|
||||||
|
cpuLimit: container.cpuLimit,
|
||||||
|
volumeMounts: container.volumeMounts,
|
||||||
|
configureSources: container.configureSources,
|
||||||
|
mountSources: container.mountSources,
|
||||||
|
sourceMapping: container.sourceMapping,
|
||||||
|
annotation: {
|
||||||
|
deployment: container.annotation.deployment,
|
||||||
|
service: container.annotation.service
|
||||||
|
},
|
||||||
|
endpoints: container.endpoints,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
addImage(image: Image): Observable<DevfileContent> {
|
addImage(image: Image): Observable<DevfileContent> {
|
||||||
return this.http.post<DevfileContent>(this.base+"/image", {
|
return this.http.post<DevfileContent>(this.base+"/image", {
|
||||||
name: image.name,
|
name: image.name,
|
||||||
|
|||||||
@@ -99,21 +99,24 @@
|
|||||||
|
|
||||||
<mat-card-actions>
|
<mat-card-actions>
|
||||||
<button mat-button color="warn" (click)="delete(container.name)">Delete</button>
|
<button mat-button color="warn" (click)="delete(container.name)">Delete</button>
|
||||||
|
<button data-cy="container-edit" mat-button (click)="edit(container)">Edit</button>
|
||||||
</mat-card-actions>
|
</mat-card-actions>
|
||||||
|
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
|
||||||
<app-container
|
<app-container
|
||||||
*ngIf="forceDisplayAdd || containers == undefined || containers.length == 0"
|
*ngIf="forceDisplayForm || containers == undefined || containers.length == 0"
|
||||||
[volumeNames]="volumeNames ?? []"
|
[volumeNames]="volumeNames ?? []"
|
||||||
[cancelable]="forceDisplayAdd"
|
[cancelable]="forceDisplayForm"
|
||||||
(canceled)="undisplayAddForm()"
|
(canceled)="undisplayAddForm()"
|
||||||
(created)="onCreated($event)"
|
(created)="onCreated($event)"
|
||||||
|
[container]="editingContainer"
|
||||||
|
(saved)="onSaved($event)"
|
||||||
></app-container>
|
></app-container>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<ng-container *ngIf="!forceDisplayAdd && containers != undefined && containers.length > 0">
|
<ng-container *ngIf="!forceDisplayForm && containers != undefined && containers.length > 0">
|
||||||
<button data-cy="add" class="fab" mat-fab color="primary" (click)="displayAddForm()">
|
<button data-cy="add" class="fab" mat-fab color="primary" (click)="displayAddForm()">
|
||||||
<mat-icon class="material-icons-outlined">add</mat-icon>
|
<mat-icon class="material-icons-outlined">add</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -11,10 +11,12 @@ import { ToCreate } from 'src/app/forms/container/container.component';
|
|||||||
})
|
})
|
||||||
export class ContainersComponent implements OnInit {
|
export class ContainersComponent implements OnInit {
|
||||||
|
|
||||||
forceDisplayAdd: boolean = false;
|
forceDisplayForm: boolean = false;
|
||||||
containers: Container[] | undefined = [];
|
containers: Container[] | undefined = [];
|
||||||
volumeNames: string[] | undefined = [];
|
volumeNames: string[] | undefined = [];
|
||||||
|
|
||||||
|
editingContainer: Container | undefined;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private state: StateService,
|
private state: StateService,
|
||||||
private devstate: DevstateService,
|
private devstate: DevstateService,
|
||||||
@@ -28,19 +30,24 @@ export class ContainersComponent implements OnInit {
|
|||||||
if (this.containers == null) {
|
if (this.containers == null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
that.forceDisplayAdd = false;
|
that.forceDisplayForm = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
displayAddForm() {
|
displayAddForm() {
|
||||||
this.forceDisplayAdd = true;
|
this.editingContainer = undefined;
|
||||||
|
this.displayForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
displayForm() {
|
||||||
|
this.forceDisplayForm = true;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.scrollToBottom();
|
this.scrollToBottom();
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
undisplayAddForm() {
|
undisplayAddForm() {
|
||||||
this.forceDisplayAdd = false;
|
this.forceDisplayForm = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(name: string) {
|
delete(name: string) {
|
||||||
@@ -73,6 +80,11 @@ export class ContainersComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
edit(container: Container) {
|
||||||
|
this.editingContainer = container;
|
||||||
|
this.displayForm();
|
||||||
|
}
|
||||||
|
|
||||||
onCreated(toCreate: ToCreate) {
|
onCreated(toCreate: ToCreate) {
|
||||||
const container = toCreate.container;
|
const container = toCreate.container;
|
||||||
this.createVolumes(toCreate.volumes, 0, () => {
|
this.createVolumes(toCreate.volumes, 0, () => {
|
||||||
@@ -86,7 +98,21 @@ export class ContainersComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onSaved(toCreate: ToCreate) {
|
||||||
|
const container = toCreate.container;
|
||||||
|
this.createVolumes(toCreate.volumes, 0, () => {
|
||||||
|
const result = this.devstate.saveContainer(container);
|
||||||
|
result.subscribe({
|
||||||
|
next: value => {
|
||||||
|
this.state.changeDevfileYaml(value);
|
||||||
|
},
|
||||||
|
error: error => {
|
||||||
|
alert(error.error.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollToBottom() {
|
scrollToBottom() {
|
||||||
|
|||||||
Reference in New Issue
Block a user