mirror of
https://github.com/redhat-developer/odo.git
synced 2025-10-19 03:06:19 +03:00
[ui] Complete container creation (#7035)
* API returns more info about container * Display more info about containers * Update UI static files * Fix unit tests * Get/Set sources configuration * [ui] create container with sources mount configuration * e2e tests + ui static files * Set containers's envvars * Regenerate UI static files * Add Annotation to POST /container * [api] Create Container with Annotations * [ui] Annotations when creating container * Regenerate UI static files * [api] Endpoints when adding container * [ui] Endpoints when adding container * Regenerate UI static files
This commit is contained in:
@@ -512,6 +512,9 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
type: object
|
type: object
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- image
|
||||||
properties:
|
properties:
|
||||||
name:
|
name:
|
||||||
description: Name of the container
|
description: Name of the container
|
||||||
@@ -531,6 +534,11 @@ paths:
|
|||||||
items: {
|
items: {
|
||||||
type: string
|
type: string
|
||||||
}
|
}
|
||||||
|
env:
|
||||||
|
description: Environment variables to define
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Env'
|
||||||
memReq:
|
memReq:
|
||||||
description: Requested memory for the deployed container
|
description: Requested memory for the deployed container
|
||||||
type: string
|
type: string
|
||||||
@@ -548,6 +556,24 @@ paths:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/VolumeMount'
|
$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:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: container was successfully added to the devfile
|
description: container was successfully added to the devfile
|
||||||
@@ -1522,6 +1548,12 @@ components:
|
|||||||
- cpuRequest
|
- cpuRequest
|
||||||
- cpuLimit
|
- cpuLimit
|
||||||
- volumeMounts
|
- volumeMounts
|
||||||
|
- annotation
|
||||||
|
- endpoints
|
||||||
|
- env
|
||||||
|
- configureSources
|
||||||
|
- mountSources
|
||||||
|
- sourceMapping
|
||||||
properties:
|
properties:
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
@@ -1547,6 +1579,22 @@ components:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/VolumeMount'
|
$ref: '#/components/schemas/VolumeMount'
|
||||||
|
annotation:
|
||||||
|
$ref: '#/components/schemas/Annotation'
|
||||||
|
endpoints:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Endpoint'
|
||||||
|
env:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Env'
|
||||||
|
configureSources:
|
||||||
|
type: boolean
|
||||||
|
mountSources:
|
||||||
|
type: boolean
|
||||||
|
sourceMapping:
|
||||||
|
type: string
|
||||||
VolumeMount:
|
VolumeMount:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
@@ -1557,6 +1605,50 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
path:
|
path:
|
||||||
type: string
|
type: string
|
||||||
|
Annotation:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- deployment
|
||||||
|
- service
|
||||||
|
properties:
|
||||||
|
deployment:
|
||||||
|
type: object
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
service:
|
||||||
|
type: object
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
Endpoint:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- targetPort
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
exposure:
|
||||||
|
type: string
|
||||||
|
enum: [public,internal,none]
|
||||||
|
path:
|
||||||
|
type: string
|
||||||
|
protocol:
|
||||||
|
type: string
|
||||||
|
enum: [http,https,ws,wss,tcp,udp]
|
||||||
|
secure:
|
||||||
|
type: boolean
|
||||||
|
targetPort:
|
||||||
|
type: integer
|
||||||
|
Env:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- value
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
value:
|
||||||
|
type: string
|
||||||
Image:
|
Image:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
|
|||||||
3
pkg/apiserver-gen/.openapi-generator/FILES
generated
3
pkg/apiserver-gen/.openapi-generator/FILES
generated
@@ -22,6 +22,7 @@ go/model__devstate_quantity_valid_post_request.go
|
|||||||
go/model__devstate_resource_post_request.go
|
go/model__devstate_resource_post_request.go
|
||||||
go/model__devstate_volume_post_request.go
|
go/model__devstate_volume_post_request.go
|
||||||
go/model__instance_get_200_response.go
|
go/model__instance_get_200_response.go
|
||||||
|
go/model_annotation.go
|
||||||
go/model_apply_command.go
|
go/model_apply_command.go
|
||||||
go/model_command.go
|
go/model_command.go
|
||||||
go/model_composite_command.go
|
go/model_composite_command.go
|
||||||
@@ -29,6 +30,8 @@ go/model_container.go
|
|||||||
go/model_devfile_content.go
|
go/model_devfile_content.go
|
||||||
go/model_devfile_put_request.go
|
go/model_devfile_put_request.go
|
||||||
go/model_devstate_devfile_put_request.go
|
go/model_devstate_devfile_put_request.go
|
||||||
|
go/model_endpoint.go
|
||||||
|
go/model_env.go
|
||||||
go/model_events.go
|
go/model_events.go
|
||||||
go/model_exec_command.go
|
go/model_exec_command.go
|
||||||
go/model_general_error.go
|
go/model_general_error.go
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ package openapi
|
|||||||
type DevstateContainerPostRequest struct {
|
type DevstateContainerPostRequest struct {
|
||||||
|
|
||||||
// Name of the container
|
// Name of the container
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name"`
|
||||||
|
|
||||||
// Container image
|
// Container image
|
||||||
Image string `json:"image,omitempty"`
|
Image string `json:"image"`
|
||||||
|
|
||||||
// Entrypoint of the container
|
// Entrypoint of the container
|
||||||
Command []string `json:"command,omitempty"`
|
Command []string `json:"command,omitempty"`
|
||||||
@@ -23,6 +23,9 @@ type DevstateContainerPostRequest struct {
|
|||||||
// Args passed to the Container entrypoint
|
// Args passed to the Container entrypoint
|
||||||
Args []string `json:"args,omitempty"`
|
Args []string `json:"args,omitempty"`
|
||||||
|
|
||||||
|
// Environment variables to define
|
||||||
|
Env []Env `json:"env,omitempty"`
|
||||||
|
|
||||||
// Requested memory for the deployed container
|
// Requested memory for the deployed container
|
||||||
MemReq string `json:"memReq,omitempty"`
|
MemReq string `json:"memReq,omitempty"`
|
||||||
|
|
||||||
@@ -37,15 +40,52 @@ type DevstateContainerPostRequest struct {
|
|||||||
|
|
||||||
// Volume to mount into the container filesystem
|
// Volume to mount into the container filesystem
|
||||||
VolumeMounts []VolumeMount `json:"volumeMounts,omitempty"`
|
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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AssertDevstateContainerPostRequestRequired checks if the required fields are not zero-ed
|
// AssertDevstateContainerPostRequestRequired checks if the required fields are not zero-ed
|
||||||
func AssertDevstateContainerPostRequestRequired(obj DevstateContainerPostRequest) error {
|
func AssertDevstateContainerPostRequestRequired(obj DevstateContainerPostRequest) error {
|
||||||
|
elements := map[string]interface{}{
|
||||||
|
"name": obj.Name,
|
||||||
|
"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 {
|
for _, el := range obj.VolumeMounts {
|
||||||
if err := AssertVolumeMountRequired(el); err != nil {
|
if err := AssertVolumeMountRequired(el); err != nil {
|
||||||
return err
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
43
pkg/apiserver-gen/go/model_annotation.go
generated
Normal file
43
pkg/apiserver-gen/go/model_annotation.go
generated
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* odo dev
|
||||||
|
*
|
||||||
|
* API interface for 'odo dev'
|
||||||
|
*
|
||||||
|
* API version: 0.1
|
||||||
|
* Generated by: OpenAPI Generator (https://openapi-generator.tech)
|
||||||
|
*/
|
||||||
|
|
||||||
|
package openapi
|
||||||
|
|
||||||
|
type Annotation struct {
|
||||||
|
Deployment map[string]string `json:"deployment"`
|
||||||
|
|
||||||
|
Service map[string]string `json:"service"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertAnnotationRequired checks if the required fields are not zero-ed
|
||||||
|
func AssertAnnotationRequired(obj Annotation) error {
|
||||||
|
elements := map[string]interface{}{
|
||||||
|
"deployment": obj.Deployment,
|
||||||
|
"service": obj.Service,
|
||||||
|
}
|
||||||
|
for name, el := range elements {
|
||||||
|
if isZero := IsZeroValue(el); isZero {
|
||||||
|
return &RequiredError{Field: name}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertRecurseAnnotationRequired recursively checks if required fields are not zero-ed in a nested slice.
|
||||||
|
// Accepts only nested slice of Annotation (e.g. [][]Annotation), otherwise ErrTypeAssertionError is thrown.
|
||||||
|
func AssertRecurseAnnotationRequired(objSlice interface{}) error {
|
||||||
|
return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error {
|
||||||
|
aAnnotation, ok := obj.(Annotation)
|
||||||
|
if !ok {
|
||||||
|
return ErrTypeAssertionError
|
||||||
|
}
|
||||||
|
return AssertAnnotationRequired(aAnnotation)
|
||||||
|
})
|
||||||
|
}
|
||||||
49
pkg/apiserver-gen/go/model_container.go
generated
49
pkg/apiserver-gen/go/model_container.go
generated
@@ -27,20 +27,38 @@ type Container struct {
|
|||||||
CpuLimit string `json:"cpuLimit"`
|
CpuLimit string `json:"cpuLimit"`
|
||||||
|
|
||||||
VolumeMounts []VolumeMount `json:"volumeMounts"`
|
VolumeMounts []VolumeMount `json:"volumeMounts"`
|
||||||
|
|
||||||
|
Annotation Annotation `json:"annotation"`
|
||||||
|
|
||||||
|
Endpoints []Endpoint `json:"endpoints"`
|
||||||
|
|
||||||
|
Env []Env `json:"env"`
|
||||||
|
|
||||||
|
ConfigureSources bool `json:"configureSources"`
|
||||||
|
|
||||||
|
MountSources bool `json:"mountSources"`
|
||||||
|
|
||||||
|
SourceMapping string `json:"sourceMapping"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AssertContainerRequired checks if the required fields are not zero-ed
|
// AssertContainerRequired checks if the required fields are not zero-ed
|
||||||
func AssertContainerRequired(obj Container) error {
|
func AssertContainerRequired(obj Container) error {
|
||||||
elements := map[string]interface{}{
|
elements := map[string]interface{}{
|
||||||
"name": obj.Name,
|
"name": obj.Name,
|
||||||
"image": obj.Image,
|
"image": obj.Image,
|
||||||
"command": obj.Command,
|
"command": obj.Command,
|
||||||
"args": obj.Args,
|
"args": obj.Args,
|
||||||
"memoryRequest": obj.MemoryRequest,
|
"memoryRequest": obj.MemoryRequest,
|
||||||
"memoryLimit": obj.MemoryLimit,
|
"memoryLimit": obj.MemoryLimit,
|
||||||
"cpuRequest": obj.CpuRequest,
|
"cpuRequest": obj.CpuRequest,
|
||||||
"cpuLimit": obj.CpuLimit,
|
"cpuLimit": obj.CpuLimit,
|
||||||
"volumeMounts": obj.VolumeMounts,
|
"volumeMounts": obj.VolumeMounts,
|
||||||
|
"annotation": obj.Annotation,
|
||||||
|
"endpoints": obj.Endpoints,
|
||||||
|
"env": obj.Env,
|
||||||
|
"configureSources": obj.ConfigureSources,
|
||||||
|
"mountSources": obj.MountSources,
|
||||||
|
"sourceMapping": obj.SourceMapping,
|
||||||
}
|
}
|
||||||
for name, el := range elements {
|
for name, el := range elements {
|
||||||
if isZero := IsZeroValue(el); isZero {
|
if isZero := IsZeroValue(el); isZero {
|
||||||
@@ -53,6 +71,19 @@ func AssertContainerRequired(obj Container) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err := AssertAnnotationRequired(obj.Annotation); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, el := range obj.Endpoints {
|
||||||
|
if err := AssertEndpointRequired(el); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, el := range obj.Env {
|
||||||
|
if err := AssertEnvRequired(el); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
51
pkg/apiserver-gen/go/model_endpoint.go
generated
Normal file
51
pkg/apiserver-gen/go/model_endpoint.go
generated
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* odo dev
|
||||||
|
*
|
||||||
|
* API interface for 'odo dev'
|
||||||
|
*
|
||||||
|
* API version: 0.1
|
||||||
|
* Generated by: OpenAPI Generator (https://openapi-generator.tech)
|
||||||
|
*/
|
||||||
|
|
||||||
|
package openapi
|
||||||
|
|
||||||
|
type Endpoint struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
Exposure string `json:"exposure,omitempty"`
|
||||||
|
|
||||||
|
Path string `json:"path,omitempty"`
|
||||||
|
|
||||||
|
Protocol string `json:"protocol,omitempty"`
|
||||||
|
|
||||||
|
Secure bool `json:"secure,omitempty"`
|
||||||
|
|
||||||
|
TargetPort int32 `json:"targetPort"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertEndpointRequired checks if the required fields are not zero-ed
|
||||||
|
func AssertEndpointRequired(obj Endpoint) error {
|
||||||
|
elements := map[string]interface{}{
|
||||||
|
"name": obj.Name,
|
||||||
|
"targetPort": obj.TargetPort,
|
||||||
|
}
|
||||||
|
for name, el := range elements {
|
||||||
|
if isZero := IsZeroValue(el); isZero {
|
||||||
|
return &RequiredError{Field: name}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertRecurseEndpointRequired recursively checks if required fields are not zero-ed in a nested slice.
|
||||||
|
// Accepts only nested slice of Endpoint (e.g. [][]Endpoint), otherwise ErrTypeAssertionError is thrown.
|
||||||
|
func AssertRecurseEndpointRequired(objSlice interface{}) error {
|
||||||
|
return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error {
|
||||||
|
aEndpoint, ok := obj.(Endpoint)
|
||||||
|
if !ok {
|
||||||
|
return ErrTypeAssertionError
|
||||||
|
}
|
||||||
|
return AssertEndpointRequired(aEndpoint)
|
||||||
|
})
|
||||||
|
}
|
||||||
43
pkg/apiserver-gen/go/model_env.go
generated
Normal file
43
pkg/apiserver-gen/go/model_env.go
generated
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* odo dev
|
||||||
|
*
|
||||||
|
* API interface for 'odo dev'
|
||||||
|
*
|
||||||
|
* API version: 0.1
|
||||||
|
* Generated by: OpenAPI Generator (https://openapi-generator.tech)
|
||||||
|
*/
|
||||||
|
|
||||||
|
package openapi
|
||||||
|
|
||||||
|
type Env struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertEnvRequired checks if the required fields are not zero-ed
|
||||||
|
func AssertEnvRequired(obj Env) error {
|
||||||
|
elements := map[string]interface{}{
|
||||||
|
"name": obj.Name,
|
||||||
|
"value": obj.Value,
|
||||||
|
}
|
||||||
|
for name, el := range elements {
|
||||||
|
if isZero := IsZeroValue(el); isZero {
|
||||||
|
return &RequiredError{Field: name}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertRecurseEnvRequired recursively checks if required fields are not zero-ed in a nested slice.
|
||||||
|
// Accepts only nested slice of Env (e.g. [][]Env), otherwise ErrTypeAssertionError is thrown.
|
||||||
|
func AssertRecurseEnvRequired(objSlice interface{}) error {
|
||||||
|
return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error {
|
||||||
|
aEnv, ok := obj.(Env)
|
||||||
|
if !ok {
|
||||||
|
return ErrTypeAssertionError
|
||||||
|
}
|
||||||
|
return AssertEnvRequired(aEnv)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -15,11 +15,17 @@ func (s *DevstateApiService) DevstateContainerPost(ctx context.Context, containe
|
|||||||
container.Image,
|
container.Image,
|
||||||
container.Command,
|
container.Command,
|
||||||
container.Args,
|
container.Args,
|
||||||
|
container.Env,
|
||||||
container.MemReq,
|
container.MemReq,
|
||||||
container.MemLimit,
|
container.MemLimit,
|
||||||
container.CpuReq,
|
container.CpuReq,
|
||||||
container.CpuLimit,
|
container.CpuLimit,
|
||||||
container.VolumeMounts,
|
container.VolumeMounts,
|
||||||
|
container.ConfigureSources,
|
||||||
|
container.MountSources,
|
||||||
|
container.SourceMapping,
|
||||||
|
container.Annotation,
|
||||||
|
container.Endpoints,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return openapi.Response(http.StatusInternalServerError, openapi.GeneralError{
|
return openapi.Response(http.StatusInternalServerError, openapi.GeneralError{
|
||||||
|
|||||||
@@ -33,11 +33,17 @@ func TestDevfileState_AddExecCommand(t *testing.T) {
|
|||||||
"an-image",
|
"an-image",
|
||||||
[]string{"run", "command"},
|
[]string{"run", "command"},
|
||||||
[]string{"arg1", "arg2"},
|
[]string{"arg1", "arg2"},
|
||||||
|
nil,
|
||||||
"1Gi",
|
"1Gi",
|
||||||
"2Gi",
|
"2Gi",
|
||||||
"100m",
|
"100m",
|
||||||
"200m",
|
"200m",
|
||||||
nil,
|
nil,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
"",
|
||||||
|
openapi.Annotation{},
|
||||||
|
nil,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -72,6 +78,7 @@ components:
|
|||||||
image: an-image
|
image: an-image
|
||||||
memoryLimit: 2Gi
|
memoryLimit: 2Gi
|
||||||
memoryRequest: 1Gi
|
memoryRequest: 1Gi
|
||||||
|
mountSources: true
|
||||||
name: a-container
|
name: a-container
|
||||||
metadata: {}
|
metadata: {}
|
||||||
schemaVersion: 2.2.0
|
schemaVersion: 2.2.0
|
||||||
@@ -90,15 +97,19 @@ schemaVersion: 2.2.0
|
|||||||
},
|
},
|
||||||
Containers: []Container{
|
Containers: []Container{
|
||||||
{
|
{
|
||||||
Name: "a-container",
|
Name: "a-container",
|
||||||
Image: "an-image",
|
Image: "an-image",
|
||||||
Command: []string{"run", "command"},
|
Command: []string{"run", "command"},
|
||||||
Args: []string{"arg1", "arg2"},
|
Args: []string{"arg1", "arg2"},
|
||||||
MemoryRequest: "1Gi",
|
MemoryRequest: "1Gi",
|
||||||
MemoryLimit: "2Gi",
|
MemoryLimit: "2Gi",
|
||||||
CpuRequest: "100m",
|
CpuRequest: "100m",
|
||||||
CpuLimit: "200m",
|
CpuLimit: "200m",
|
||||||
VolumeMounts: []openapi.VolumeMount{},
|
VolumeMounts: []openapi.VolumeMount{},
|
||||||
|
Endpoints: []openapi.Endpoint{},
|
||||||
|
Env: []openapi.Env{},
|
||||||
|
ConfigureSources: true,
|
||||||
|
MountSources: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Images: []Image{},
|
Images: []Image{},
|
||||||
@@ -236,11 +247,17 @@ func TestDevfileState_AddCompositeCommand(t *testing.T) {
|
|||||||
"an-image",
|
"an-image",
|
||||||
[]string{"run", "command"},
|
[]string{"run", "command"},
|
||||||
[]string{"arg1", "arg2"},
|
[]string{"arg1", "arg2"},
|
||||||
|
nil,
|
||||||
"1Gi",
|
"1Gi",
|
||||||
"2Gi",
|
"2Gi",
|
||||||
"100m",
|
"100m",
|
||||||
"200m",
|
"200m",
|
||||||
nil,
|
nil,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
"",
|
||||||
|
openapi.Annotation{},
|
||||||
|
nil,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -288,6 +305,7 @@ components:
|
|||||||
image: an-image
|
image: an-image
|
||||||
memoryLimit: 2Gi
|
memoryLimit: 2Gi
|
||||||
memoryRequest: 1Gi
|
memoryRequest: 1Gi
|
||||||
|
mountSources: true
|
||||||
name: a-container
|
name: a-container
|
||||||
metadata: {}
|
metadata: {}
|
||||||
schemaVersion: 2.2.0
|
schemaVersion: 2.2.0
|
||||||
@@ -311,15 +329,19 @@ schemaVersion: 2.2.0
|
|||||||
},
|
},
|
||||||
Containers: []Container{
|
Containers: []Container{
|
||||||
{
|
{
|
||||||
Name: "a-container",
|
Name: "a-container",
|
||||||
Image: "an-image",
|
Image: "an-image",
|
||||||
Command: []string{"run", "command"},
|
Command: []string{"run", "command"},
|
||||||
Args: []string{"arg1", "arg2"},
|
Args: []string{"arg1", "arg2"},
|
||||||
MemoryRequest: "1Gi",
|
MemoryRequest: "1Gi",
|
||||||
MemoryLimit: "2Gi",
|
MemoryLimit: "2Gi",
|
||||||
CpuRequest: "100m",
|
CpuRequest: "100m",
|
||||||
CpuLimit: "200m",
|
CpuLimit: "200m",
|
||||||
VolumeMounts: []openapi.VolumeMount{},
|
VolumeMounts: []openapi.VolumeMount{},
|
||||||
|
Endpoints: []openapi.Endpoint{},
|
||||||
|
Env: []openapi.Env{},
|
||||||
|
ConfigureSources: true,
|
||||||
|
MountSources: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Images: []Image{},
|
Images: []Image{},
|
||||||
@@ -368,11 +390,17 @@ func TestDevfileState_DeleteCommand(t *testing.T) {
|
|||||||
"an-image",
|
"an-image",
|
||||||
[]string{"run", "command"},
|
[]string{"run", "command"},
|
||||||
[]string{"arg1", "arg2"},
|
[]string{"arg1", "arg2"},
|
||||||
|
nil,
|
||||||
"1Gi",
|
"1Gi",
|
||||||
"2Gi",
|
"2Gi",
|
||||||
"100m",
|
"100m",
|
||||||
"200m",
|
"200m",
|
||||||
nil,
|
nil,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
"",
|
||||||
|
openapi.Annotation{},
|
||||||
|
nil,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -406,6 +434,7 @@ func TestDevfileState_DeleteCommand(t *testing.T) {
|
|||||||
image: an-image
|
image: an-image
|
||||||
memoryLimit: 2Gi
|
memoryLimit: 2Gi
|
||||||
memoryRequest: 1Gi
|
memoryRequest: 1Gi
|
||||||
|
mountSources: true
|
||||||
name: a-container
|
name: a-container
|
||||||
metadata: {}
|
metadata: {}
|
||||||
schemaVersion: 2.2.0
|
schemaVersion: 2.2.0
|
||||||
@@ -413,15 +442,19 @@ schemaVersion: 2.2.0
|
|||||||
Commands: []Command{},
|
Commands: []Command{},
|
||||||
Containers: []Container{
|
Containers: []Container{
|
||||||
{
|
{
|
||||||
Name: "a-container",
|
Name: "a-container",
|
||||||
Image: "an-image",
|
Image: "an-image",
|
||||||
Command: []string{"run", "command"},
|
Command: []string{"run", "command"},
|
||||||
Args: []string{"arg1", "arg2"},
|
Args: []string{"arg1", "arg2"},
|
||||||
MemoryRequest: "1Gi",
|
MemoryRequest: "1Gi",
|
||||||
MemoryLimit: "2Gi",
|
MemoryLimit: "2Gi",
|
||||||
CpuRequest: "100m",
|
CpuRequest: "100m",
|
||||||
CpuLimit: "200m",
|
CpuLimit: "200m",
|
||||||
VolumeMounts: []openapi.VolumeMount{},
|
VolumeMounts: []openapi.VolumeMount{},
|
||||||
|
Endpoints: []openapi.Endpoint{},
|
||||||
|
Env: []openapi.Env{},
|
||||||
|
ConfigureSources: true,
|
||||||
|
MountSources: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Images: []Image{},
|
Images: []Image{},
|
||||||
|
|||||||
@@ -14,11 +14,17 @@ func (o *DevfileState) AddContainer(
|
|||||||
image string,
|
image string,
|
||||||
command []string,
|
command []string,
|
||||||
args []string,
|
args []string,
|
||||||
|
envs []Env,
|
||||||
memRequest string,
|
memRequest string,
|
||||||
memLimit string,
|
memLimit string,
|
||||||
cpuRequest string,
|
cpuRequest string,
|
||||||
cpuLimit string,
|
cpuLimit string,
|
||||||
volumeMounts []VolumeMount,
|
volumeMounts []VolumeMount,
|
||||||
|
configureSources bool,
|
||||||
|
mountSources bool,
|
||||||
|
sourceMapping string,
|
||||||
|
annotation Annotation,
|
||||||
|
endpoints []Endpoint,
|
||||||
) (DevfileContent, error) {
|
) (DevfileContent, error) {
|
||||||
v1alpha2VolumeMounts := make([]v1alpha2.VolumeMount, 0, len(volumeMounts))
|
v1alpha2VolumeMounts := make([]v1alpha2.VolumeMount, 0, len(volumeMounts))
|
||||||
for _, vm := range volumeMounts {
|
for _, vm := range volumeMounts {
|
||||||
@@ -27,6 +33,38 @@ func (o *DevfileState) AddContainer(
|
|||||||
Path: vm.Path,
|
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,
|
||||||
ComponentUnion: v1alpha2.ComponentUnion{
|
ComponentUnion: v1alpha2.ComponentUnion{
|
||||||
@@ -35,15 +73,22 @@ func (o *DevfileState) AddContainer(
|
|||||||
Image: image,
|
Image: image,
|
||||||
Command: command,
|
Command: command,
|
||||||
Args: args,
|
Args: args,
|
||||||
|
Env: v1alpha2Envs,
|
||||||
MemoryRequest: memRequest,
|
MemoryRequest: memRequest,
|
||||||
MemoryLimit: memLimit,
|
MemoryLimit: memLimit,
|
||||||
CpuRequest: cpuRequest,
|
CpuRequest: cpuRequest,
|
||||||
CpuLimit: cpuLimit,
|
CpuLimit: cpuLimit,
|
||||||
VolumeMounts: v1alpha2VolumeMounts,
|
VolumeMounts: v1alpha2VolumeMounts,
|
||||||
|
Annotation: annotations,
|
||||||
},
|
},
|
||||||
|
Endpoints: v1alpha2Endpoints,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
if configureSources {
|
||||||
|
container.Container.MountSources = &mountSources
|
||||||
|
container.Container.SourceMapping = sourceMapping
|
||||||
|
}
|
||||||
err := o.Devfile.Data.AddComponents([]v1alpha2.Component{container})
|
err := o.Devfile.Data.AddComponents([]v1alpha2.Component{container})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return DevfileContent{}, err
|
return DevfileContent{}, err
|
||||||
|
|||||||
@@ -10,15 +10,21 @@ import (
|
|||||||
|
|
||||||
func TestDevfileState_AddContainer(t *testing.T) {
|
func TestDevfileState_AddContainer(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
name string
|
name string
|
||||||
image string
|
image string
|
||||||
command []string
|
command []string
|
||||||
args []string
|
args []string
|
||||||
memRequest string
|
envs []Env
|
||||||
memLimit string
|
memRequest string
|
||||||
cpuRequest string
|
memLimit string
|
||||||
cpuLimit string
|
cpuRequest string
|
||||||
volumeMounts []openapi.VolumeMount
|
cpuLimit string
|
||||||
|
volumeMounts []openapi.VolumeMount
|
||||||
|
configureSources bool
|
||||||
|
mountSources bool
|
||||||
|
sourceMapping string
|
||||||
|
annotation Annotation
|
||||||
|
endpoints []Endpoint
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@@ -28,7 +34,7 @@ func TestDevfileState_AddContainer(t *testing.T) {
|
|||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Add a container",
|
name: "Add a container, with sources configured",
|
||||||
state: func() DevfileState {
|
state: func() DevfileState {
|
||||||
return NewDevfileState()
|
return NewDevfileState()
|
||||||
},
|
},
|
||||||
@@ -47,6 +53,81 @@ func TestDevfileState_AddContainer(t *testing.T) {
|
|||||||
Path: "/mnt/volume1",
|
Path: "/mnt/volume1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
configureSources: true,
|
||||||
|
mountSources: false,
|
||||||
|
},
|
||||||
|
want: DevfileContent{
|
||||||
|
Content: `components:
|
||||||
|
- container:
|
||||||
|
args:
|
||||||
|
- arg1
|
||||||
|
- arg2
|
||||||
|
command:
|
||||||
|
- run
|
||||||
|
- command
|
||||||
|
cpuLimit: 200m
|
||||||
|
cpuRequest: 100m
|
||||||
|
image: an-image
|
||||||
|
memoryLimit: 2Gi
|
||||||
|
memoryRequest: 1Gi
|
||||||
|
mountSources: false
|
||||||
|
volumeMounts:
|
||||||
|
- name: vol1
|
||||||
|
path: /mnt/volume1
|
||||||
|
name: a-name
|
||||||
|
metadata: {}
|
||||||
|
schemaVersion: 2.2.0
|
||||||
|
`,
|
||||||
|
Commands: []Command{},
|
||||||
|
Containers: []Container{
|
||||||
|
{
|
||||||
|
Name: "a-name",
|
||||||
|
Image: "an-image",
|
||||||
|
Command: []string{"run", "command"},
|
||||||
|
Args: []string{"arg1", "arg2"},
|
||||||
|
MemoryRequest: "1Gi",
|
||||||
|
MemoryLimit: "2Gi",
|
||||||
|
CpuRequest: "100m",
|
||||||
|
CpuLimit: "200m",
|
||||||
|
VolumeMounts: []openapi.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: "vol1",
|
||||||
|
Path: "/mnt/volume1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Endpoints: []openapi.Endpoint{},
|
||||||
|
Env: []openapi.Env{},
|
||||||
|
ConfigureSources: true,
|
||||||
|
MountSources: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Images: []Image{},
|
||||||
|
Resources: []Resource{},
|
||||||
|
Volumes: []Volume{},
|
||||||
|
Events: Events{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Add a container, without sources configured",
|
||||||
|
state: func() DevfileState {
|
||||||
|
return NewDevfileState()
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
name: "a-name",
|
||||||
|
image: "an-image",
|
||||||
|
command: []string{"run", "command"},
|
||||||
|
args: []string{"arg1", "arg2"},
|
||||||
|
memRequest: "1Gi",
|
||||||
|
memLimit: "2Gi",
|
||||||
|
cpuRequest: "100m",
|
||||||
|
cpuLimit: "200m",
|
||||||
|
volumeMounts: []openapi.VolumeMount{
|
||||||
|
{
|
||||||
|
Name: "vol1",
|
||||||
|
Path: "/mnt/volume1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
configureSources: false,
|
||||||
},
|
},
|
||||||
want: DevfileContent{
|
want: DevfileContent{
|
||||||
Content: `components:
|
Content: `components:
|
||||||
@@ -86,6 +167,10 @@ schemaVersion: 2.2.0
|
|||||||
Path: "/mnt/volume1",
|
Path: "/mnt/volume1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Endpoints: []openapi.Endpoint{},
|
||||||
|
Env: []openapi.Env{},
|
||||||
|
ConfigureSources: false,
|
||||||
|
MountSources: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Images: []Image{},
|
Images: []Image{},
|
||||||
@@ -99,7 +184,7 @@ schemaVersion: 2.2.0
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
o := tt.state()
|
o := tt.state()
|
||||||
got, err := o.AddContainer(tt.args.name, tt.args.image, tt.args.command, tt.args.args, tt.args.memRequest, tt.args.memLimit, tt.args.cpuRequest, tt.args.cpuLimit, tt.args.volumeMounts)
|
got, err := o.AddContainer(tt.args.name, tt.args.image, tt.args.command, tt.args.args, tt.args.envs, tt.args.memRequest, tt.args.memLimit, tt.args.cpuRequest, tt.args.cpuLimit, tt.args.volumeMounts, tt.args.configureSources, tt.args.mountSources, tt.args.sourceMapping, tt.args.annotation, tt.args.endpoints)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
t.Errorf("DevfileState.AddContainer() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("DevfileState.AddContainer() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
return
|
return
|
||||||
@@ -134,11 +219,17 @@ func TestDevfileState_DeleteContainer(t *testing.T) {
|
|||||||
"an-image",
|
"an-image",
|
||||||
[]string{"run", "command"},
|
[]string{"run", "command"},
|
||||||
[]string{"arg1", "arg2"},
|
[]string{"arg1", "arg2"},
|
||||||
|
nil,
|
||||||
"1Gi",
|
"1Gi",
|
||||||
"2Gi",
|
"2Gi",
|
||||||
"100m",
|
"100m",
|
||||||
"200m",
|
"200m",
|
||||||
nil,
|
nil,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
"",
|
||||||
|
Annotation{},
|
||||||
|
nil,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -169,11 +260,17 @@ schemaVersion: 2.2.0
|
|||||||
"an-image",
|
"an-image",
|
||||||
[]string{"run", "command"},
|
[]string{"run", "command"},
|
||||||
[]string{"arg1", "arg2"},
|
[]string{"arg1", "arg2"},
|
||||||
|
nil,
|
||||||
"1Gi",
|
"1Gi",
|
||||||
"2Gi",
|
"2Gi",
|
||||||
"100m",
|
"100m",
|
||||||
"200m",
|
"200m",
|
||||||
nil,
|
nil,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
"",
|
||||||
|
Annotation{},
|
||||||
|
nil,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|||||||
@@ -162,15 +162,21 @@ func (o *DevfileState) getContainers() ([]Container, error) {
|
|||||||
result := make([]Container, 0, len(containers))
|
result := make([]Container, 0, len(containers))
|
||||||
for _, container := range containers {
|
for _, container := range containers {
|
||||||
result = append(result, Container{
|
result = append(result, Container{
|
||||||
Name: container.Name,
|
Name: container.Name,
|
||||||
Image: container.ComponentUnion.Container.Image,
|
Image: container.ComponentUnion.Container.Image,
|
||||||
Command: container.ComponentUnion.Container.Command,
|
Command: container.ComponentUnion.Container.Command,
|
||||||
Args: container.ComponentUnion.Container.Args,
|
Args: container.ComponentUnion.Container.Args,
|
||||||
MemoryRequest: container.ComponentUnion.Container.MemoryRequest,
|
MemoryRequest: container.ComponentUnion.Container.MemoryRequest,
|
||||||
MemoryLimit: container.ComponentUnion.Container.MemoryLimit,
|
MemoryLimit: container.ComponentUnion.Container.MemoryLimit,
|
||||||
CpuRequest: container.ComponentUnion.Container.CpuRequest,
|
CpuRequest: container.ComponentUnion.Container.CpuRequest,
|
||||||
CpuLimit: container.ComponentUnion.Container.CpuLimit,
|
CpuLimit: container.ComponentUnion.Container.CpuLimit,
|
||||||
VolumeMounts: o.getVolumeMounts(container.Container.Container),
|
VolumeMounts: o.getVolumeMounts(container.Container.Container),
|
||||||
|
Annotation: o.getAnnotation(container.Container.Annotation),
|
||||||
|
Endpoints: o.getEndpoints(container.Container.Endpoints),
|
||||||
|
Env: o.getEnv(container.Container.Env),
|
||||||
|
ConfigureSources: container.Container.MountSources != nil,
|
||||||
|
MountSources: pointer.BoolDeref(container.Container.MountSources, true), // TODO(feloy) default value will depend on dedicatedPod
|
||||||
|
SourceMapping: container.Container.SourceMapping,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -187,6 +193,42 @@ func (o *DevfileState) getVolumeMounts(container v1alpha2.Container) []VolumeMou
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *DevfileState) getAnnotation(annotation *v1alpha2.Annotation) Annotation {
|
||||||
|
if annotation == nil {
|
||||||
|
return Annotation{}
|
||||||
|
}
|
||||||
|
return Annotation{
|
||||||
|
Deployment: annotation.Deployment,
|
||||||
|
Service: annotation.Service,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *DevfileState) getEndpoints(endpoints []v1alpha2.Endpoint) []Endpoint {
|
||||||
|
result := make([]Endpoint, 0, len(endpoints))
|
||||||
|
for _, ep := range endpoints {
|
||||||
|
result = append(result, Endpoint{
|
||||||
|
Name: ep.Name,
|
||||||
|
Exposure: string(ep.Exposure),
|
||||||
|
Path: ep.Path,
|
||||||
|
Protocol: string(ep.Protocol),
|
||||||
|
Secure: pointer.BoolDeref(ep.Secure, false),
|
||||||
|
TargetPort: int32(ep.TargetPort),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *DevfileState) getEnv(envs []v1alpha2.EnvVar) []Env {
|
||||||
|
result := make([]Env, 0, len(envs))
|
||||||
|
for _, env := range envs {
|
||||||
|
result = append(result, Env{
|
||||||
|
Name: env.Name,
|
||||||
|
Value: env.Value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
func (o *DevfileState) getImages() ([]Image, error) {
|
func (o *DevfileState) getImages() ([]Image, error) {
|
||||||
images, err := o.Devfile.Data.GetComponents(common.DevfileOptions{
|
images, err := o.Devfile.Data.GetComponents(common.DevfileOptions{
|
||||||
ComponentOptions: common.ComponentOptions{
|
ComponentOptions: common.ComponentOptions{
|
||||||
|
|||||||
@@ -512,6 +512,9 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
type: object
|
type: object
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- image
|
||||||
properties:
|
properties:
|
||||||
name:
|
name:
|
||||||
description: Name of the container
|
description: Name of the container
|
||||||
@@ -531,6 +534,11 @@ paths:
|
|||||||
items: {
|
items: {
|
||||||
type: string
|
type: string
|
||||||
}
|
}
|
||||||
|
env:
|
||||||
|
description: Environment variables to define
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Env'
|
||||||
memReq:
|
memReq:
|
||||||
description: Requested memory for the deployed container
|
description: Requested memory for the deployed container
|
||||||
type: string
|
type: string
|
||||||
@@ -548,6 +556,24 @@ paths:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/VolumeMount'
|
$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:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: container was successfully added to the devfile
|
description: container was successfully added to the devfile
|
||||||
@@ -1522,6 +1548,12 @@ components:
|
|||||||
- cpuRequest
|
- cpuRequest
|
||||||
- cpuLimit
|
- cpuLimit
|
||||||
- volumeMounts
|
- volumeMounts
|
||||||
|
- annotation
|
||||||
|
- endpoints
|
||||||
|
- env
|
||||||
|
- configureSources
|
||||||
|
- mountSources
|
||||||
|
- sourceMapping
|
||||||
properties:
|
properties:
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
@@ -1547,6 +1579,22 @@ components:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/VolumeMount'
|
$ref: '#/components/schemas/VolumeMount'
|
||||||
|
annotation:
|
||||||
|
$ref: '#/components/schemas/Annotation'
|
||||||
|
endpoints:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Endpoint'
|
||||||
|
env:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Env'
|
||||||
|
configureSources:
|
||||||
|
type: boolean
|
||||||
|
mountSources:
|
||||||
|
type: boolean
|
||||||
|
sourceMapping:
|
||||||
|
type: string
|
||||||
VolumeMount:
|
VolumeMount:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
@@ -1557,6 +1605,50 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
path:
|
path:
|
||||||
type: string
|
type: string
|
||||||
|
Annotation:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- deployment
|
||||||
|
- service
|
||||||
|
properties:
|
||||||
|
deployment:
|
||||||
|
type: object
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
service:
|
||||||
|
type: object
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
Endpoint:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- targetPort
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
exposure:
|
||||||
|
type: string
|
||||||
|
enum: [public,internal,none]
|
||||||
|
path:
|
||||||
|
type: string
|
||||||
|
protocol:
|
||||||
|
type: string
|
||||||
|
enum: [http,https,ws,wss,tcp,udp]
|
||||||
|
secure:
|
||||||
|
type: boolean
|
||||||
|
targetPort:
|
||||||
|
type: integer
|
||||||
|
Env:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- value
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
value:
|
||||||
|
type: string
|
||||||
Image:
|
Image:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
|
|||||||
4
pkg/apiserver-impl/ui/index.html
generated
4
pkg/apiserver-impl/ui/index.html
generated
File diff suppressed because one or more lines are too long
1
pkg/apiserver-impl/ui/main.1046d99cec4375b1.js
generated
1
pkg/apiserver-impl/ui/main.1046d99cec4375b1.js
generated
File diff suppressed because one or more lines are too long
1
pkg/apiserver-impl/ui/main.fcdb01b1f229861f.js
generated
Normal file
1
pkg/apiserver-impl/ui/main.fcdb01b1f229861f.js
generated
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -39,7 +39,7 @@ describe('devfile editor spec', () => {
|
|||||||
.should('contain.text', 'with arg');
|
.should('contain.text', 'with arg');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('displays a created container', () => {
|
it('displays a created container without source configuration', () => {
|
||||||
cy.init();
|
cy.init();
|
||||||
|
|
||||||
cy.selectTab(TAB_VOLUMES);
|
cy.selectTab(TAB_VOLUMES);
|
||||||
@@ -51,6 +51,87 @@ describe('devfile editor spec', () => {
|
|||||||
cy.selectTab(TAB_CONTAINERS);
|
cy.selectTab(TAB_CONTAINERS);
|
||||||
cy.getByDataCy('container-name').type('created-container');
|
cy.getByDataCy('container-name').type('created-container');
|
||||||
cy.getByDataCy('container-image').type('an-image');
|
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");
|
||||||
|
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");
|
||||||
|
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-info').first()
|
||||||
|
.should('contain.text', 'created-container')
|
||||||
|
.should('contain.text', 'an-image')
|
||||||
|
.should('contain.text', 'VAR1: val1')
|
||||||
|
.should('contain.text', 'VAR2: val2')
|
||||||
|
.should('contain.text', 'VAR3: val3')
|
||||||
|
.should('contain.text', 'volume1')
|
||||||
|
.should('contain.text', '/mnt/vol1')
|
||||||
|
.should('contain.text', 'volume2')
|
||||||
|
.should('contain.text', '/mnt/vol2')
|
||||||
|
.should('not.contain.text', 'Mount Sources')
|
||||||
|
.should('contain.text', 'ep1')
|
||||||
|
.should('contain.text', '4001')
|
||||||
|
.should('contain.text', 'Deployment Annotations')
|
||||||
|
.should('contain.text', 'DEPANNO1: depval1')
|
||||||
|
.should('contain.text', 'DEPANNO2: depval2')
|
||||||
|
.should('contain.text', 'Service Annotations')
|
||||||
|
.should('contain.text', 'SVCANNO1: svcval1')
|
||||||
|
.should('contain.text', 'SVCANNO2: svcval2');
|
||||||
|
|
||||||
|
cy.selectTab(TAB_VOLUMES);
|
||||||
|
cy.getByDataCy('volume-info').eq(1)
|
||||||
|
.should('contain.text', 'volume2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays a created container with source configuration', () => {
|
||||||
|
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-more-params').click();
|
||||||
|
cy.getByDataCy('container-sources-configuration').click();
|
||||||
|
cy.getByDataCy('container-sources-specific-directory').click();
|
||||||
|
cy.getByDataCy('container-source-mapping').type('/mnt/sources');
|
||||||
|
|
||||||
cy.getByDataCy('volume-mount-add').click();
|
cy.getByDataCy('volume-mount-add').click();
|
||||||
cy.getByDataCy('volume-mount-path-0').type("/mnt/vol1");
|
cy.getByDataCy('volume-mount-path-0').type("/mnt/vol1");
|
||||||
@@ -70,7 +151,9 @@ describe('devfile editor spec', () => {
|
|||||||
.should('contain.text', 'volume1')
|
.should('contain.text', 'volume1')
|
||||||
.should('contain.text', '/mnt/vol1')
|
.should('contain.text', '/mnt/vol1')
|
||||||
.should('contain.text', 'volume2')
|
.should('contain.text', 'volume2')
|
||||||
.should('contain.text', '/mnt/vol2');
|
.should('contain.text', '/mnt/vol2')
|
||||||
|
.should('contain.text', 'Mount Sources')
|
||||||
|
.should('contain.text', '/mnt/sources');
|
||||||
|
|
||||||
cy.selectTab(TAB_VOLUMES);
|
cy.selectTab(TAB_VOLUMES);
|
||||||
cy.getByDataCy('volume-info').eq(1)
|
cy.getByDataCy('volume-info').eq(1)
|
||||||
|
|||||||
3
ui/src/app/api-gen/.openapi-generator/FILES
generated
3
ui/src/app/api-gen/.openapi-generator/FILES
generated
@@ -8,6 +8,7 @@ configuration.ts
|
|||||||
encoder.ts
|
encoder.ts
|
||||||
git_push.sh
|
git_push.sh
|
||||||
index.ts
|
index.ts
|
||||||
|
model/annotation.ts
|
||||||
model/applyCommand.ts
|
model/applyCommand.ts
|
||||||
model/command.ts
|
model/command.ts
|
||||||
model/componentCommandPostRequest.ts
|
model/componentCommandPostRequest.ts
|
||||||
@@ -30,6 +31,8 @@ model/devstateImagePostRequest.ts
|
|||||||
model/devstateQuantityValidPostRequest.ts
|
model/devstateQuantityValidPostRequest.ts
|
||||||
model/devstateResourcePostRequest.ts
|
model/devstateResourcePostRequest.ts
|
||||||
model/devstateVolumePostRequest.ts
|
model/devstateVolumePostRequest.ts
|
||||||
|
model/endpoint.ts
|
||||||
|
model/env.ts
|
||||||
model/events.ts
|
model/events.ts
|
||||||
model/execCommand.ts
|
model/execCommand.ts
|
||||||
model/generalError.ts
|
model/generalError.ts
|
||||||
|
|||||||
18
ui/src/app/api-gen/model/annotation.ts
generated
Normal file
18
ui/src/app/api-gen/model/annotation.ts
generated
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
export interface Annotation {
|
||||||
|
deployment: { [key: string]: string; };
|
||||||
|
service: { [key: string]: string; };
|
||||||
|
}
|
||||||
|
|
||||||
9
ui/src/app/api-gen/model/container.ts
generated
9
ui/src/app/api-gen/model/container.ts
generated
@@ -9,7 +9,10 @@
|
|||||||
* https://openapi-generator.tech
|
* https://openapi-generator.tech
|
||||||
* Do not edit the class manually.
|
* Do not edit the class manually.
|
||||||
*/
|
*/
|
||||||
|
import { Endpoint } from './endpoint';
|
||||||
import { VolumeMount } from './volumeMount';
|
import { VolumeMount } from './volumeMount';
|
||||||
|
import { Env } from './env';
|
||||||
|
import { Annotation } from './annotation';
|
||||||
|
|
||||||
|
|
||||||
export interface Container {
|
export interface Container {
|
||||||
@@ -22,5 +25,11 @@ export interface Container {
|
|||||||
cpuRequest: string;
|
cpuRequest: string;
|
||||||
cpuLimit: string;
|
cpuLimit: string;
|
||||||
volumeMounts: Array<VolumeMount>;
|
volumeMounts: Array<VolumeMount>;
|
||||||
|
annotation: Annotation;
|
||||||
|
endpoints: Array<Endpoint>;
|
||||||
|
env: Array<Env>;
|
||||||
|
configureSources: boolean;
|
||||||
|
mountSources: boolean;
|
||||||
|
sourceMapping: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,18 +9,21 @@
|
|||||||
* https://openapi-generator.tech
|
* https://openapi-generator.tech
|
||||||
* Do not edit the class manually.
|
* Do not edit the class manually.
|
||||||
*/
|
*/
|
||||||
|
import { Endpoint } from './endpoint';
|
||||||
import { VolumeMount } from './volumeMount';
|
import { VolumeMount } from './volumeMount';
|
||||||
|
import { Env } from './env';
|
||||||
|
import { Annotation } from './annotation';
|
||||||
|
|
||||||
|
|
||||||
export interface DevstateContainerPostRequest {
|
export interface DevstateContainerPostRequest {
|
||||||
/**
|
/**
|
||||||
* Name of the container
|
* Name of the container
|
||||||
*/
|
*/
|
||||||
name?: string;
|
name: string;
|
||||||
/**
|
/**
|
||||||
* Container image
|
* Container image
|
||||||
*/
|
*/
|
||||||
image?: string;
|
image: string;
|
||||||
/**
|
/**
|
||||||
* Entrypoint of the container
|
* Entrypoint of the container
|
||||||
*/
|
*/
|
||||||
@@ -29,6 +32,10 @@ export interface DevstateContainerPostRequest {
|
|||||||
* Args passed to the Container entrypoint
|
* Args passed to the Container entrypoint
|
||||||
*/
|
*/
|
||||||
args?: Array<string>;
|
args?: Array<string>;
|
||||||
|
/**
|
||||||
|
* Environment variables to define
|
||||||
|
*/
|
||||||
|
env?: Array<Env>;
|
||||||
/**
|
/**
|
||||||
* Requested memory for the deployed container
|
* Requested memory for the deployed container
|
||||||
*/
|
*/
|
||||||
@@ -49,5 +56,22 @@ export interface DevstateContainerPostRequest {
|
|||||||
* Volume to mount into the container filesystem
|
* Volume to mount into the container filesystem
|
||||||
*/
|
*/
|
||||||
volumeMounts?: Array<VolumeMount>;
|
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>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
40
ui/src/app/api-gen/model/endpoint.ts
generated
Normal file
40
ui/src/app/api-gen/model/endpoint.ts
generated
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
export interface Endpoint {
|
||||||
|
name: string;
|
||||||
|
exposure?: Endpoint.ExposureEnum;
|
||||||
|
path?: string;
|
||||||
|
protocol?: Endpoint.ProtocolEnum;
|
||||||
|
secure?: boolean;
|
||||||
|
targetPort: number;
|
||||||
|
}
|
||||||
|
export namespace Endpoint {
|
||||||
|
export type ExposureEnum = 'public' | 'internal' | 'none';
|
||||||
|
export const ExposureEnum = {
|
||||||
|
Public: 'public' as ExposureEnum,
|
||||||
|
Internal: 'internal' as ExposureEnum,
|
||||||
|
None: 'none' as ExposureEnum
|
||||||
|
};
|
||||||
|
export type ProtocolEnum = 'http' | 'https' | 'ws' | 'wss' | 'tcp' | 'udp';
|
||||||
|
export const ProtocolEnum = {
|
||||||
|
Http: 'http' as ProtocolEnum,
|
||||||
|
Https: 'https' as ProtocolEnum,
|
||||||
|
Ws: 'ws' as ProtocolEnum,
|
||||||
|
Wss: 'wss' as ProtocolEnum,
|
||||||
|
Tcp: 'tcp' as ProtocolEnum,
|
||||||
|
Udp: 'udp' as ProtocolEnum
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
18
ui/src/app/api-gen/model/env.ts
generated
Normal file
18
ui/src/app/api-gen/model/env.ts
generated
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
export interface Env {
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
3
ui/src/app/api-gen/model/models.ts
generated
3
ui/src/app/api-gen/model/models.ts
generated
@@ -1,3 +1,4 @@
|
|||||||
|
export * from './annotation';
|
||||||
export * from './applyCommand';
|
export * from './applyCommand';
|
||||||
export * from './command';
|
export * from './command';
|
||||||
export * from './componentCommandPostRequest';
|
export * from './componentCommandPostRequest';
|
||||||
@@ -20,6 +21,8 @@ export * from './devstateImagePostRequest';
|
|||||||
export * from './devstateQuantityValidPostRequest';
|
export * from './devstateQuantityValidPostRequest';
|
||||||
export * from './devstateResourcePostRequest';
|
export * from './devstateResourcePostRequest';
|
||||||
export * from './devstateVolumePostRequest';
|
export * from './devstateVolumePostRequest';
|
||||||
|
export * from './endpoint';
|
||||||
|
export * from './env';
|
||||||
export * from './events';
|
export * from './events';
|
||||||
export * from './execCommand';
|
export * from './execCommand';
|
||||||
export * from './generalError';
|
export * from './generalError';
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ import { ConfirmComponent } from './components/confirm/confirm.component';
|
|||||||
import { VolumesComponent } from './tabs/volumes/volumes.component';
|
import { VolumesComponent } from './tabs/volumes/volumes.component';
|
||||||
import { VolumeComponent } from './forms/volume/volume.component';
|
import { VolumeComponent } from './forms/volume/volume.component';
|
||||||
import { VolumeMountsComponent } from './controls/volume-mounts/volume-mounts.component';
|
import { VolumeMountsComponent } from './controls/volume-mounts/volume-mounts.component';
|
||||||
|
import { MultiKeyValueComponent } from './controls/multi-key-value/multi-key-value.component';
|
||||||
|
import { EndpointsComponent } from './controls/endpoints/endpoints.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@@ -74,6 +76,8 @@ import { VolumeMountsComponent } from './controls/volume-mounts/volume-mounts.co
|
|||||||
VolumesComponent,
|
VolumesComponent,
|
||||||
VolumeComponent,
|
VolumeComponent,
|
||||||
VolumeMountsComponent,
|
VolumeMountsComponent,
|
||||||
|
MultiKeyValueComponent,
|
||||||
|
EndpointsComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
|
|||||||
2
ui/src/app/controls/endpoints/endpoints.component.css
Normal file
2
ui/src/app/controls/endpoints/endpoints.component.css
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
.mid-width { width: 50%; }
|
||||||
|
.quart-width { width: 25%; }
|
||||||
43
ui/src/app/controls/endpoints/endpoints.component.html
Normal file
43
ui/src/app/controls/endpoints/endpoints.component.html
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<div *ngFor="let control of form.controls; index as i">
|
||||||
|
<ng-container [formGroup]="control">
|
||||||
|
<mat-form-field class="mid-width" appearance="outline">
|
||||||
|
<mat-label><span>Name</span></mat-label>
|
||||||
|
<input [attr.data-cy]="'endpoint-name-'+i" matInput formControlName="name">
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field class="quart-width" appearance="outline">
|
||||||
|
<mat-label><span>Target Port</span></mat-label>
|
||||||
|
<input [attr.data-cy]="'endpoint-targetPort-'+i" type="number" matInput formControlName="targetPort">
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field class="quart-width" appearance="outline">
|
||||||
|
<mat-label>Exposure</mat-label>
|
||||||
|
<mat-select [attr.data-cy]="'endpoint-exposure-'+i" formControlName="exposure">
|
||||||
|
<mat-option value="">(default, public)</mat-option>
|
||||||
|
<mat-option value="public">public</mat-option>
|
||||||
|
<mat-option value="internal">internal</mat-option>
|
||||||
|
<mat-option value="none">none</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field class="mid-width" appearance="outline">
|
||||||
|
<mat-label><span>Path</span></mat-label>
|
||||||
|
<input [attr.data-cy]="'endpoint-path-'+i" matInput formControlName="path">
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field class="quart-width" appearance="outline">
|
||||||
|
<mat-label>Protocol</mat-label>
|
||||||
|
<mat-select [attr.data-cy]="'endpoint-protocol-'+i" formControlName="protocol">
|
||||||
|
<mat-option value="">(default, http)</mat-option>
|
||||||
|
<mat-option value="http">http</mat-option>
|
||||||
|
<mat-option value="https">https</mat-option>
|
||||||
|
<mat-option value="ws">ws</mat-option>
|
||||||
|
<mat-option value="wss">wss</mat-option>
|
||||||
|
<mat-option value="tcp">tcp</mat-option>
|
||||||
|
<mat-option value="udp">udp</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-checkbox [attr.data-cy]="'endpoint-secure-'+i" formControlName="secure">Protocol Is Secure</mat-checkbox>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
<button data-cy="endpoints-plus" *ngIf="form.value.length > 0" mat-icon-button (click)="addEndpoint()">
|
||||||
|
<mat-icon class="tab-icon material-icons-outlined">add</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button data-cy="endpoints-add" *ngIf="form.value.length == 0" mat-flat-button (click)="addEndpoint()">Add an Endpoint</button>
|
||||||
23
ui/src/app/controls/endpoints/endpoints.component.spec.ts
Normal file
23
ui/src/app/controls/endpoints/endpoints.component.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { EndpointsComponent } from './endpoints.component';
|
||||||
|
|
||||||
|
describe('EndpointsComponent', () => {
|
||||||
|
let component: EndpointsComponent;
|
||||||
|
let fixture: ComponentFixture<EndpointsComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ EndpointsComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(EndpointsComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
74
ui/src/app/controls/endpoints/endpoints.component.ts
Normal file
74
ui/src/app/controls/endpoints/endpoints.component.ts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import { Component, forwardRef } from '@angular/core';
|
||||||
|
import { AbstractControl, ControlValueAccessor, FormArray, FormControl, FormGroup, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, Validator, Validators } from '@angular/forms';
|
||||||
|
|
||||||
|
interface Endpoint {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-endpoints',
|
||||||
|
templateUrl: './endpoints.component.html',
|
||||||
|
styleUrls: ['./endpoints.component.css'],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
multi: true,
|
||||||
|
useExisting: EndpointsComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: NG_VALIDATORS,
|
||||||
|
useExisting: forwardRef(() => EndpointsComponent),
|
||||||
|
multi: true,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class EndpointsComponent implements ControlValueAccessor, Validator {
|
||||||
|
|
||||||
|
onChange = (_: Endpoint[]) => {};
|
||||||
|
onValidatorChange = () => {};
|
||||||
|
|
||||||
|
form = new FormArray<FormGroup>([]);
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.form.valueChanges.subscribe(value => {
|
||||||
|
this.onChange(value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
newEndpoint(): FormGroup {
|
||||||
|
return new FormGroup({
|
||||||
|
name: new FormControl("", [Validators.required]),
|
||||||
|
targetPort: new FormControl("", [Validators.required, Validators.pattern("^[0-9]*$")]),
|
||||||
|
exposure: new FormControl(""),
|
||||||
|
path: new FormControl(""),
|
||||||
|
protocol: new FormControl(""),
|
||||||
|
secure: new FormControl(false),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addEndpoint() {
|
||||||
|
this.form.push(this.newEndpoint());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ControlValueAccessor implementation */
|
||||||
|
writeValue(value: Endpoint[]) {
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange(onChange: any) {
|
||||||
|
this.onChange = onChange;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched(_: any) {}
|
||||||
|
|
||||||
|
/* Validator implementation */
|
||||||
|
validate(control: AbstractControl): ValidationErrors | null {
|
||||||
|
if (!this.form.valid) {
|
||||||
|
return {'internal': true};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnValidatorChange?(onValidatorChange: () => void): void {
|
||||||
|
this.onValidatorChange = onValidatorChange;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
div.group { margin-bottom: 16px; }
|
||||||
|
.mid-width { width: 50%; }
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<div class="group">
|
||||||
|
<span *ngFor="let entry of entries; let i=index">
|
||||||
|
<mat-form-field class="mid-width" appearance="outline">
|
||||||
|
<mat-label><span>Name</span></mat-label>
|
||||||
|
<input [attr.data-cy]="dataCyPrefix+'-name-'+i" matInput [value]="entry.name" (change)="onKeyChange(i, $event)" (input)="onKeyChange(i, $event)">
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field class="mid-width" appearance="outline">
|
||||||
|
<mat-label><span>Value</span></mat-label>
|
||||||
|
<input [attr.data-cy]="dataCyPrefix+'-value-'+i" matInput [value]="entry.value" (change)="onValueChange(i, $event)" (input)="onValueChange(i, $event)">
|
||||||
|
</mat-form-field>
|
||||||
|
</span>
|
||||||
|
<button [attr.data-cy]="dataCyPrefix+'-plus'" *ngIf="entries.length > 0" mat-icon-button (click)="addEntry()">
|
||||||
|
<mat-icon class="tab-icon material-icons-outlined">add</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button [attr.data-cy]="dataCyPrefix+'-add'" *ngIf="entries.length == 0" mat-flat-button (click)="addEntry()">{{addLabel}}</button>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { MultiKeyValueComponent } from './multi-key-value.component';
|
||||||
|
|
||||||
|
describe('MultiKeyValueComponent', () => {
|
||||||
|
let component: MultiKeyValueComponent;
|
||||||
|
let fixture: ComponentFixture<MultiKeyValueComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ MultiKeyValueComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(MultiKeyValueComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import { Component, Input, forwardRef } from '@angular/core';
|
||||||
|
import { AbstractControl, ControlValueAccessor, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, Validator, Validators } from '@angular/forms';
|
||||||
|
|
||||||
|
interface KeyValue {
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-multi-key-value',
|
||||||
|
templateUrl: './multi-key-value.component.html',
|
||||||
|
styleUrls: ['./multi-key-value.component.css'],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
multi: true,
|
||||||
|
useExisting: MultiKeyValueComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: NG_VALIDATORS,
|
||||||
|
useExisting: forwardRef(() => MultiKeyValueComponent),
|
||||||
|
multi: true,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class MultiKeyValueComponent implements ControlValueAccessor, Validator {
|
||||||
|
|
||||||
|
@Input() dataCyPrefix: string = "";
|
||||||
|
@Input() addLabel: string = "";
|
||||||
|
|
||||||
|
onChange = (_: KeyValue[]) => {};
|
||||||
|
onValidatorChange = () => {};
|
||||||
|
|
||||||
|
entries: KeyValue[] = [];
|
||||||
|
|
||||||
|
writeValue(value: KeyValue[]) {
|
||||||
|
this.entries = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange(onChange: any) {
|
||||||
|
this.onChange = onChange;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched(_: any) {}
|
||||||
|
|
||||||
|
addEntry() {
|
||||||
|
this.entries.push({name: "", value: ""});
|
||||||
|
this.onChange(this.entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyChange(i: number, e: Event) {
|
||||||
|
const target = e.target as HTMLInputElement;
|
||||||
|
this.entries[i].name = target.value;
|
||||||
|
this.onChange(this.entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
onValueChange(i: number, e: Event) {
|
||||||
|
const target = e.target as HTMLInputElement;
|
||||||
|
this.entries[i].value = target.value;
|
||||||
|
this.onChange(this.entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validator implementation */
|
||||||
|
validate(control: AbstractControl): ValidationErrors | null {
|
||||||
|
for (let i=0; i<this.entries.length; i++) {
|
||||||
|
const entry = this.entries[i];
|
||||||
|
if (entry.name == "" || entry.value == "") {
|
||||||
|
return {'internal': true};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnValidatorChange?(onValidatorChange: () => void): void {
|
||||||
|
this.onValidatorChange = onValidatorChange;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,7 +12,6 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
|||||||
useExisting: MultiTextComponent
|
useExisting: MultiTextComponent
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
})
|
})
|
||||||
export class MultiTextComponent implements ControlValueAccessor {
|
export class MultiTextComponent implements ControlValueAccessor {
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
<h3>Volume Mounts</h3>
|
|
||||||
<div class="group">
|
<div class="group">
|
||||||
<div *ngFor="let vm of volumeMounts; let i=index">
|
<div *ngFor="let vm of volumeMounts; let i=index">
|
||||||
<mat-form-field class="inline" appearance="outline">
|
<mat-form-field class="inline" appearance="outline">
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import { Volume, VolumeMount } from 'src/app/api-gen';
|
|||||||
provide: NG_VALIDATORS,
|
provide: NG_VALIDATORS,
|
||||||
useExisting: forwardRef(() => VolumeMountsComponent),
|
useExisting: forwardRef(() => VolumeMountsComponent),
|
||||||
multi: true,
|
multi: true,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class VolumeMountsComponent implements Validator {
|
export class VolumeMountsComponent implements Validator {
|
||||||
|
|||||||
@@ -1,3 +1,12 @@
|
|||||||
.main { padding: 16px; }
|
.main { padding: 16px; }
|
||||||
mat-form-field.full-width { width: 100%; }
|
mat-form-field.full-width { width: 100%; }
|
||||||
mat-form-field.mid-width { width: 50%; }
|
.mid-width { width: 50%; }
|
||||||
|
.source-configuration-details {
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
|
div.buttonbar {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
.outbutton {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
@@ -12,36 +12,81 @@
|
|||||||
<input placeholder="Image to start the container" data-cy="container-image" matInput formControlName="image">
|
<input placeholder="Image to start the container" data-cy="container-image" matInput formControlName="image">
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<h3>Command and Arguments</h3>
|
<h3>Command and Arguments</h3>
|
||||||
|
<div class="description">Command and Arguments can be used to override the entrypoint of the image</div>
|
||||||
<app-multi-text formControlName="command" label="Command" addLabel="Add command"></app-multi-text>
|
<app-multi-text formControlName="command" label="Command" addLabel="Add command"></app-multi-text>
|
||||||
<app-multi-text formControlName="args" label="Arg" addLabel="Add arg"></app-multi-text>
|
<app-multi-text formControlName="args" label="Arg" addLabel="Add arg"></app-multi-text>
|
||||||
|
|
||||||
|
<h3>Environment Variables</h3>
|
||||||
|
<div class="description">Environment Variables to define in the running container</div>
|
||||||
|
<app-multi-key-value dataCyPrefix="container-env" addLabel="Add Environment Variable" formControlName="env"></app-multi-key-value>
|
||||||
|
|
||||||
|
<h3>Volume Mounts</h3>
|
||||||
|
<div class="description">Volumes to mount into the container's filesystem</div>
|
||||||
<app-volume-mounts
|
<app-volume-mounts
|
||||||
[volumes]="volumeNames"
|
[volumes]="volumeNames"
|
||||||
formControlName="volumeMounts"
|
formControlName="volumeMounts"
|
||||||
(createNewVolume)="onCreateNewVolume($event)"></app-volume-mounts>
|
(createNewVolume)="onCreateNewVolume($event)"></app-volume-mounts>
|
||||||
|
|
||||||
<h3>Resource Usage</h3>
|
|
||||||
<mat-form-field appearance="outline" class="mid-width">
|
<h3>Endpoints</h3>
|
||||||
<mat-label><span>Memory Request</span></mat-label>
|
<div class="description">Endpoints exposed by the container</div>
|
||||||
<mat-error>{{quantityErrMsgMemory}}</mat-error>
|
<app-endpoints formControlName="endpoints"></app-endpoints>
|
||||||
<input placeholder="memory requested for the container. Ex: 1Gi" data-cy="container-memory-request" matInput formControlName="memoryRequest">
|
|
||||||
</mat-form-field>
|
<div class="outbutton"><button data-cy="container-more-params" *ngIf="!seeMore" mat-flat-button (click)="more()">More parameters...</button></div>
|
||||||
<mat-form-field appearance="outline" class="mid-width">
|
|
||||||
<mat-label><span>Memory Limit</span></mat-label>
|
<div *ngIf="seeMore">
|
||||||
<mat-error>{{quantityErrMsgMemory}}</mat-error>
|
|
||||||
<input placeholder="memory limit for the container. Ex: 1Gi" data-cy="container-memory-limit" matInput formControlName="memoryLimit">
|
<h3>Resource Usage</h3>
|
||||||
</mat-form-field>
|
<div class="description">CPU and Memory resources necessary for container's execution</div>
|
||||||
<mat-form-field appearance="outline" class="mid-width">
|
<mat-form-field appearance="outline" class="mid-width">
|
||||||
<mat-label><span>CPU Request</span></mat-label>
|
<mat-label><span>Memory Request</span></mat-label>
|
||||||
<mat-error>{{quantityErrMsgCPU}}</mat-error>
|
<mat-error>{{quantityErrMsgMemory}}</mat-error>
|
||||||
<input placeholder="CPU requested for the container. Ex: 500m" data-cy="container-cpu-request" matInput formControlName="cpuRequest">
|
<input placeholder="memory requested for the container. Ex: 1Gi" data-cy="container-memory-request" matInput formControlName="memoryRequest">
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-form-field appearance="outline" class="mid-width">
|
<mat-form-field appearance="outline" class="mid-width">
|
||||||
<mat-label><span>CPU Limit</span></mat-label>
|
<mat-label><span>Memory Limit</span></mat-label>
|
||||||
<mat-error>{{quantityErrMsgCPU}}</mat-error>
|
<mat-error>{{quantityErrMsgMemory}}</mat-error>
|
||||||
<input placeholder="CPU limit for the container. Ex: 1" data-cy="container-cpu-limit" matInput formControlName="cpuLimit">
|
<input placeholder="memory limit for the container. Ex: 1Gi" data-cy="container-memory-limit" matInput formControlName="memoryLimit">
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
<mat-form-field appearance="outline" class="mid-width">
|
||||||
|
<mat-label><span>CPU Request</span></mat-label>
|
||||||
|
<mat-error>{{quantityErrMsgCPU}}</mat-error>
|
||||||
|
<input placeholder="CPU requested for the container. Ex: 500m" data-cy="container-cpu-request" matInput formControlName="cpuRequest">
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field appearance="outline" class="mid-width">
|
||||||
|
<mat-label><span>CPU Limit</span></mat-label>
|
||||||
|
<mat-error>{{quantityErrMsgCPU}}</mat-error>
|
||||||
|
<input placeholder="CPU limit for the container. Ex: 1" data-cy="container-cpu-limit" matInput formControlName="cpuLimit">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<h3>Sources</h3>
|
||||||
|
<div class="description">Declare if and how sources are mounted into the container's filesystem. By default, sources are automatically mounted into $PROJECTS_ROOT or /projects directory</div>
|
||||||
|
<div><mat-checkbox data-cy="container-sources-configuration" formControlName="configureSources">Configure Source mount</mat-checkbox></div>
|
||||||
|
<div *ngIf="form.get('configureSources')?.value" class="source-configuration-details">
|
||||||
|
<div style="display: inline-flex" class="mid-width">
|
||||||
|
<mat-checkbox data-cy="container-mount-sources" formControlName="mountSources">Mount sources into container</mat-checkbox>
|
||||||
|
<mat-checkbox data-cy="container-sources-specific-directory" matTooltip="${PROJECTS_ROOT} or /projects by default" formControlName="_specificDir">Into specific directory</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<mat-form-field appearance="outline" class="mid-width">
|
||||||
|
<mat-label><span>Mount sources into</span></mat-label>
|
||||||
|
<input placeholder="Container's directory on which to mount sources" data-cy="container-source-mapping" matInput formControlName="sourceMapping">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>Deployment Annotations</h3>
|
||||||
|
<div class="description">Annotations added to the Kubernetes Deployment created for running this container</div>
|
||||||
|
<app-multi-key-value dataCyPrefix="container-deploy-anno" addLabel="Add Annotation" formControlName="deployAnnotations"></app-multi-key-value>
|
||||||
|
|
||||||
|
<h3>Service Annotations</h3>
|
||||||
|
<div class="description">Annotations added to the Kubernetes Service created for accessing this container</div>
|
||||||
|
<app-multi-key-value dataCyPrefix="container-svc-anno" addLabel="Add Annotation" formControlName="svcAnnotations"></app-multi-key-value>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="outbutton"><button data-cy="container-less-params" *ngIf="seeMore" mat-flat-button (click)="less()">Less parameters...</button></div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<button data-cy="container-create" [disabled]="form.invalid" mat-flat-button color="primary" matTooltip="create new container" (click)="create()">Create</button>
|
<div class="buttonbar">
|
||||||
<button *ngIf="cancelable" mat-flat-button (click)="cancel()">Cancel</button>
|
<button data-cy="container-create" [disabled]="form.invalid" mat-flat-button color="primary" matTooltip="create new container" (click)="create()">Create</button>
|
||||||
|
<button *ngIf="cancelable" mat-flat-button (click)="cancel()">Cancel</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ export class ContainerComponent {
|
|||||||
quantityErrMsgCPU = 'Numeric value, with optional unit m, k, M, G, T, P, E';
|
quantityErrMsgCPU = 'Numeric value, with optional unit m, k, M, G, T, P, E';
|
||||||
|
|
||||||
volumesToCreate: Volume[] = [];
|
volumesToCreate: Volume[] = [];
|
||||||
|
seeMore: boolean = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private devstate: DevstateService,
|
private devstate: DevstateService,
|
||||||
@@ -37,16 +38,59 @@ export class ContainerComponent {
|
|||||||
image: new FormControl("", [Validators.required]),
|
image: new FormControl("", [Validators.required]),
|
||||||
command: new FormControl([]),
|
command: new FormControl([]),
|
||||||
args: new FormControl([]),
|
args: new FormControl([]),
|
||||||
|
env: new FormControl([]),
|
||||||
|
volumeMounts: new FormControl([]),
|
||||||
memoryRequest: new FormControl("", null, [this.devstate.isQuantity()]),
|
memoryRequest: new FormControl("", null, [this.devstate.isQuantity()]),
|
||||||
memoryLimit: new FormControl("", null, [this.devstate.isQuantity()]),
|
memoryLimit: new FormControl("", null, [this.devstate.isQuantity()]),
|
||||||
cpuRequest: new FormControl("", null, [this.devstate.isQuantity()]),
|
cpuRequest: new FormControl("", null, [this.devstate.isQuantity()]),
|
||||||
cpuLimit: new FormControl("", null, [this.devstate.isQuantity()]),
|
cpuLimit: new FormControl("", null, [this.devstate.isQuantity()]),
|
||||||
volumeMounts: new FormControl([]),
|
configureSources: new FormControl(false),
|
||||||
})
|
mountSources: new FormControl(true),
|
||||||
|
_specificDir: new FormControl(false),
|
||||||
|
sourceMapping: new FormControl(""),
|
||||||
|
deployAnnotations: new FormControl([]),
|
||||||
|
svcAnnotations: new FormControl([]),
|
||||||
|
endpoints: new FormControl([]),
|
||||||
|
});
|
||||||
|
|
||||||
|
this.form.valueChanges.subscribe((value: any) => {
|
||||||
|
this.updateSourceFields(value);
|
||||||
|
});
|
||||||
|
this.updateSourceFields(this.form.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSourceFields(value: any) {
|
||||||
|
const sourceMappingEnabled = value.mountSources && value._specificDir;
|
||||||
|
if (!sourceMappingEnabled && !this.form.get('sourceMapping')?.disabled) {
|
||||||
|
this.form.get('sourceMapping')?.disable();
|
||||||
|
this.form.get('sourceMapping')?.setValue('');
|
||||||
|
this.form.get('_specificDir')?.setValue(false);
|
||||||
|
}
|
||||||
|
if (sourceMappingEnabled && !this.form.get('sourceMapping')?.enabled ) {
|
||||||
|
this.form.get('sourceMapping')?.enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
const specificDirEnabled = value.mountSources;
|
||||||
|
if (!specificDirEnabled && !this.form.get('_specificDir')?.disabled) {
|
||||||
|
this.form.get('_specificDir')?.disable();
|
||||||
|
}
|
||||||
|
if (specificDirEnabled && !this.form.get('_specificDir')?.enabled ) {
|
||||||
|
this.form.get('_specificDir')?.enable();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
container.annotation = {
|
||||||
|
deployment: toObject(container.deployAnnotations),
|
||||||
|
service: toObject(container.svcAnnotations),
|
||||||
|
};
|
||||||
this.created.emit({
|
this.created.emit({
|
||||||
container: this.form.value,
|
container: this.form.value,
|
||||||
volumes: this.volumesToCreate,
|
volumes: this.volumesToCreate,
|
||||||
@@ -60,4 +104,11 @@ export class ContainerComponent {
|
|||||||
onCreateNewVolume(v: Volume) {
|
onCreateNewVolume(v: Volume) {
|
||||||
this.volumesToCreate.push(v);
|
this.volumesToCreate.push(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
more() {
|
||||||
|
this.seeMore = true;
|
||||||
|
}
|
||||||
|
less() {
|
||||||
|
this.seeMore = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,11 +19,20 @@ export class DevstateService {
|
|||||||
image: container.image,
|
image: container.image,
|
||||||
command: container.command,
|
command: container.command,
|
||||||
args: container.args,
|
args: container.args,
|
||||||
|
env: container.env,
|
||||||
memReq: container.memoryRequest,
|
memReq: container.memoryRequest,
|
||||||
memLimit: container.memoryLimit,
|
memLimit: container.memoryLimit,
|
||||||
cpuReq: container.cpuRequest,
|
cpuReq: container.cpuRequest,
|
||||||
cpuLimit: container.cpuLimit,
|
cpuLimit: container.cpuLimit,
|
||||||
volumeMounts: container.volumeMounts,
|
volumeMounts: container.volumeMounts,
|
||||||
|
configureSources: container.configureSources,
|
||||||
|
mountSources: container.mountSources,
|
||||||
|
sourceMapping: container.sourceMapping,
|
||||||
|
annotation: {
|
||||||
|
deployment: container.annotation.deployment,
|
||||||
|
service: container.annotation.service
|
||||||
|
},
|
||||||
|
endpoints: container.endpoints,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,3 +14,26 @@ mat-card-content { padding: 16px; }
|
|||||||
table.aligned > tr > td {
|
table.aligned > tr > td {
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.endpoint-list {
|
||||||
|
display: float;
|
||||||
|
}
|
||||||
|
mat-card.endpoint {
|
||||||
|
width: fit-content;
|
||||||
|
float: left;
|
||||||
|
margin: 0 8px;
|
||||||
|
}
|
||||||
|
mat-card.endpoint mat-card-header {
|
||||||
|
padding: 8px 8px 0 8px;
|
||||||
|
}
|
||||||
|
mat-card.endpoint mat-card-title {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
mat-card.endpoint mat-card-subtitle {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
mat-card.endpoint mat-card-content {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,6 +18,14 @@
|
|||||||
<td>Args:</td>
|
<td>Args:</td>
|
||||||
<td><code>{{container.args.join(" ")}}</code></td>
|
<td><code>{{container.args.join(" ")}}</code></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr *ngIf="container.env.length">
|
||||||
|
<td>Environment variables:</td>
|
||||||
|
<td>
|
||||||
|
<div *ngFor="let env of container.env">
|
||||||
|
{{env.name}}: {{env.value}}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr *ngIf="container.volumeMounts.length > 0">
|
<tr *ngIf="container.volumeMounts.length > 0">
|
||||||
<td>Volume Mounts:</td>
|
<td>Volume Mounts:</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -44,6 +52,47 @@
|
|||||||
<td>CPU Limit:</td>
|
<td>CPU Limit:</td>
|
||||||
<td><code>{{container.cpuLimit}}</code></td>
|
<td><code>{{container.cpuLimit}}</code></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr *ngIf="container.annotation.deployment">
|
||||||
|
<td>Deployment Annotations:</td>
|
||||||
|
<td>
|
||||||
|
<div *ngFor="let anno of container.annotation.deployment | keyvalue">
|
||||||
|
{{anno.key}}: {{anno.value}}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr *ngIf="container.annotation.service">
|
||||||
|
<td>Service Annotations:</td>
|
||||||
|
<td>
|
||||||
|
<div *ngFor="let anno of container.annotation.service | keyvalue">
|
||||||
|
{{anno.key}}: {{anno.value}}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr *ngIf="container.configureSources">
|
||||||
|
<td>Mount Sources:</td>
|
||||||
|
<td><code>{{container.mountSources ? "Yes" : "No"}}</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr *ngIf="container.configureSources && container.mountSources && container.sourceMapping">
|
||||||
|
<td>Mount Sources Into:</td>
|
||||||
|
<td><code>{{container.sourceMapping}}</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr *ngIf="container.endpoints.length">
|
||||||
|
<td>Endpoints:</td>
|
||||||
|
<td class="container-list">
|
||||||
|
<mat-card class="endpoint" *ngFor="let ep of container.endpoints">
|
||||||
|
<mat-card-header>
|
||||||
|
<mat-card-title>{{ep.name}}</mat-card-title>
|
||||||
|
<mat-card-subtitle>{{ep.targetPort}}</mat-card-subtitle>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-card-content>
|
||||||
|
<div>exposure: {{ep.exposure ?? 'public'}}</div>
|
||||||
|
<div>protocol: {{ep.protocol ?? 'http'}}</div>
|
||||||
|
<div *ngIf="ep.secure">secure</div>
|
||||||
|
<div *ngIf="ep.path">path: {{ep.path}}</div>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
|
|||||||
@@ -25,6 +25,14 @@ h2:has(+.description) {
|
|||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
color: #3f51b5;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3:has(+.description) {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.description {
|
.description {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-size: smaller;
|
font-size: smaller;
|
||||||
|
|||||||
Reference in New Issue
Block a user