[ui] Create/Delete volumes (#7029)

* [api/devstate] Add volumes to Devfile content

* Add Volume related endpoints to API

* Create/Delete volumes from the Volumes Tab

* Update UI static files

* API Devstate returns VolumeMounts

* Display volume mounts in containers

* [api] Add VolumeMounts to containers

* [ui] Define container's volume mounts

* [ui] e2e  tests

* Update UI static files

* [ui] create volumes from container / exec command creation

* Update UI static files

* Update container display

* Update UI static files

* Regenerate UI static files
This commit is contained in:
Philippe Martin
2023-08-21 18:02:55 +02:00
committed by GitHub
parent fcc1cd880d
commit edf0bf38d4
54 changed files with 1596 additions and 152 deletions

View File

@@ -20,6 +20,7 @@ go/model__devstate_exec_command_post_request.go
go/model__devstate_image_post_request.go
go/model__devstate_quantity_valid_post_request.go
go/model__devstate_resource_post_request.go
go/model__devstate_volume_post_request.go
go/model__instance_get_200_response.go
go/model_apply_command.go
go/model_command.go
@@ -38,3 +39,5 @@ go/model_metadata.go
go/model_metadata_request.go
go/model_resource.go
go/model_telemetry_response.go
go/model_volume.go
go/model_volume_mount.go

View File

@@ -51,6 +51,8 @@ type DevstateApiRouter interface {
DevstateQuantityValidPost(http.ResponseWriter, *http.Request)
DevstateResourcePost(http.ResponseWriter, *http.Request)
DevstateResourceResourceNameDelete(http.ResponseWriter, *http.Request)
DevstateVolumePost(http.ResponseWriter, *http.Request)
DevstateVolumeVolumeNameDelete(http.ResponseWriter, *http.Request)
}
// DefaultApiServicer defines the api actions for the DefaultApi service
@@ -92,4 +94,6 @@ type DevstateApiServicer interface {
DevstateQuantityValidPost(context.Context, DevstateQuantityValidPostRequest) (ImplResponse, error)
DevstateResourcePost(context.Context, DevstateResourcePostRequest) (ImplResponse, error)
DevstateResourceResourceNameDelete(context.Context, string) (ImplResponse, error)
DevstateVolumePost(context.Context, DevstateVolumePostRequest) (ImplResponse, error)
DevstateVolumeVolumeNameDelete(context.Context, string) (ImplResponse, error)
}

View File

@@ -170,6 +170,18 @@ func (c *DevstateApiController) Routes() Routes {
"/api/v1/devstate/resource/{resourceName}",
c.DevstateResourceResourceNameDelete,
},
{
"DevstateVolumePost",
strings.ToUpper("Post"),
"/api/v1/devstate/volume",
c.DevstateVolumePost,
},
{
"DevstateVolumeVolumeNameDelete",
strings.ToUpper("Delete"),
"/api/v1/devstate/volume/{volumeName}",
c.DevstateVolumeVolumeNameDelete,
},
}
}
@@ -578,3 +590,42 @@ func (c *DevstateApiController) DevstateResourceResourceNameDelete(w http.Respon
EncodeJSONResponse(result.Body, &result.Code, w)
}
// DevstateVolumePost -
func (c *DevstateApiController) DevstateVolumePost(w http.ResponseWriter, r *http.Request) {
devstateVolumePostRequestParam := DevstateVolumePostRequest{}
d := json.NewDecoder(r.Body)
d.DisallowUnknownFields()
if err := d.Decode(&devstateVolumePostRequestParam); err != nil {
c.errorHandler(w, r, &ParsingError{Err: err}, nil)
return
}
if err := AssertDevstateVolumePostRequestRequired(devstateVolumePostRequestParam); err != nil {
c.errorHandler(w, r, err, nil)
return
}
result, err := c.service.DevstateVolumePost(r.Context(), devstateVolumePostRequestParam)
// 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)
}
// DevstateVolumeVolumeNameDelete -
func (c *DevstateApiController) DevstateVolumeVolumeNameDelete(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
volumeNameParam := params["volumeName"]
result, err := c.service.DevstateVolumeVolumeNameDelete(r.Context(), volumeNameParam)
// 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)
}

View File

@@ -34,10 +34,18 @@ type DevstateContainerPostRequest struct {
// CPU limit for the deployed container
CpuLimit string `json:"cpuLimit,omitempty"`
// Volume to mount into the container filesystem
VolumeMounts []VolumeMount `json:"volumeMounts,omitempty"`
}
// AssertDevstateContainerPostRequestRequired checks if the required fields are not zero-ed
func AssertDevstateContainerPostRequestRequired(obj DevstateContainerPostRequest) error {
for _, el := range obj.VolumeMounts {
if err := AssertVolumeMountRequired(el); err != nil {
return err
}
}
return nil
}

View File

@@ -0,0 +1,39 @@
/*
* odo dev
*
* API interface for 'odo dev'
*
* API version: 0.1
* Generated by: OpenAPI Generator (https://openapi-generator.tech)
*/
package openapi
type DevstateVolumePostRequest struct {
// Name of the volume
Name string `json:"name,omitempty"`
// Minimal size of the volume
Size string `json:"size,omitempty"`
// True if the Volume is Ephemeral
Ephemeral bool `json:"ephemeral,omitempty"`
}
// AssertDevstateVolumePostRequestRequired checks if the required fields are not zero-ed
func AssertDevstateVolumePostRequestRequired(obj DevstateVolumePostRequest) error {
return nil
}
// AssertRecurseDevstateVolumePostRequestRequired recursively checks if required fields are not zero-ed in a nested slice.
// Accepts only nested slice of DevstateVolumePostRequest (e.g. [][]DevstateVolumePostRequest), otherwise ErrTypeAssertionError is thrown.
func AssertRecurseDevstateVolumePostRequestRequired(objSlice interface{}) error {
return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error {
aDevstateVolumePostRequest, ok := obj.(DevstateVolumePostRequest)
if !ok {
return ErrTypeAssertionError
}
return AssertDevstateVolumePostRequestRequired(aDevstateVolumePostRequest)
})
}

View File

@@ -25,6 +25,8 @@ type Container struct {
CpuRequest string `json:"cpuRequest"`
CpuLimit string `json:"cpuLimit"`
VolumeMounts []VolumeMount `json:"volumeMounts"`
}
// AssertContainerRequired checks if the required fields are not zero-ed
@@ -38,6 +40,7 @@ func AssertContainerRequired(obj Container) error {
"memoryLimit": obj.MemoryLimit,
"cpuRequest": obj.CpuRequest,
"cpuLimit": obj.CpuLimit,
"volumeMounts": obj.VolumeMounts,
}
for name, el := range elements {
if isZero := IsZeroValue(el); isZero {
@@ -45,6 +48,11 @@ func AssertContainerRequired(obj Container) error {
}
}
for _, el := range obj.VolumeMounts {
if err := AssertVolumeMountRequired(el); err != nil {
return err
}
}
return nil
}

View File

@@ -20,6 +20,8 @@ type DevfileContent struct {
Resources []Resource `json:"resources"`
Volumes []Volume `json:"volumes"`
Events Events `json:"events"`
Metadata Metadata `json:"metadata"`
@@ -33,6 +35,7 @@ func AssertDevfileContentRequired(obj DevfileContent) error {
"containers": obj.Containers,
"images": obj.Images,
"resources": obj.Resources,
"volumes": obj.Volumes,
"events": obj.Events,
"metadata": obj.Metadata,
}
@@ -62,6 +65,11 @@ func AssertDevfileContentRequired(obj DevfileContent) error {
return err
}
}
for _, el := range obj.Volumes {
if err := AssertVolumeRequired(el); err != nil {
return err
}
}
if err := AssertEventsRequired(obj.Events); err != nil {
return err
}

44
pkg/apiserver-gen/go/model_volume.go generated Normal file
View File

@@ -0,0 +1,44 @@
/*
* odo dev
*
* API interface for 'odo dev'
*
* API version: 0.1
* Generated by: OpenAPI Generator (https://openapi-generator.tech)
*/
package openapi
type Volume struct {
Name string `json:"name"`
Ephemeral bool `json:"ephemeral,omitempty"`
Size string `json:"size,omitempty"`
}
// AssertVolumeRequired checks if the required fields are not zero-ed
func AssertVolumeRequired(obj Volume) error {
elements := map[string]interface{}{
"name": obj.Name,
}
for name, el := range elements {
if isZero := IsZeroValue(el); isZero {
return &RequiredError{Field: name}
}
}
return nil
}
// AssertRecurseVolumeRequired recursively checks if required fields are not zero-ed in a nested slice.
// Accepts only nested slice of Volume (e.g. [][]Volume), otherwise ErrTypeAssertionError is thrown.
func AssertRecurseVolumeRequired(objSlice interface{}) error {
return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error {
aVolume, ok := obj.(Volume)
if !ok {
return ErrTypeAssertionError
}
return AssertVolumeRequired(aVolume)
})
}

View 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 VolumeMount struct {
Name string `json:"name"`
Path string `json:"path"`
}
// AssertVolumeMountRequired checks if the required fields are not zero-ed
func AssertVolumeMountRequired(obj VolumeMount) error {
elements := map[string]interface{}{
"name": obj.Name,
"path": obj.Path,
}
for name, el := range elements {
if isZero := IsZeroValue(el); isZero {
return &RequiredError{Field: name}
}
}
return nil
}
// AssertRecurseVolumeMountRequired recursively checks if required fields are not zero-ed in a nested slice.
// Accepts only nested slice of VolumeMount (e.g. [][]VolumeMount), otherwise ErrTypeAssertionError is thrown.
func AssertRecurseVolumeMountRequired(objSlice interface{}) error {
return AssertRecurseInterfaceRequired(objSlice, func(obj interface{}) error {
aVolumeMount, ok := obj.(VolumeMount)
if !ok {
return ErrTypeAssertionError
}
return AssertVolumeMountRequired(aVolumeMount)
})
}

View File

@@ -19,6 +19,7 @@ func (s *DevstateApiService) DevstateContainerPost(ctx context.Context, containe
container.MemLimit,
container.CpuReq,
container.CpuLimit,
container.VolumeMounts,
)
if err != nil {
return openapi.Response(http.StatusInternalServerError, openapi.GeneralError{
@@ -90,6 +91,30 @@ func (s *DevstateApiService) DevstateResourceResourceNameDelete(ctx context.Cont
return openapi.Response(http.StatusOK, newContent), nil
}
func (s *DevstateApiService) DevstateVolumePost(ctx context.Context, volume openapi.DevstateVolumePostRequest) (openapi.ImplResponse, error) {
newContent, err := s.devfileState.AddVolume(
volume.Name,
volume.Ephemeral,
volume.Size,
)
if err != nil {
return openapi.Response(http.StatusInternalServerError, openapi.GeneralError{
Message: fmt.Sprintf("Error adding the volume: %s", err),
}), nil
}
return openapi.Response(http.StatusOK, newContent), nil
}
func (s *DevstateApiService) DevstateVolumeVolumeNameDelete(ctx context.Context, volumeName string) (openapi.ImplResponse, error) {
newContent, err := s.devfileState.DeleteVolume(volumeName)
if err != nil {
return openapi.Response(http.StatusInternalServerError, openapi.GeneralError{
Message: fmt.Sprintf("Error deleting the volume: %s", err),
}), nil
}
return openapi.Response(http.StatusOK, newContent), nil
}
func (s *DevstateApiService) DevstateApplyCommandPost(ctx context.Context, command openapi.DevstateApplyCommandPostRequest) (openapi.ImplResponse, error) {
newContent, err := s.devfileState.AddApplyCommand(
command.Name,

View File

@@ -6,6 +6,7 @@ import (
"github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
"github.com/google/go-cmp/cmp"
. "github.com/redhat-developer/odo/pkg/apiserver-gen/go"
openapi "github.com/redhat-developer/odo/pkg/apiserver-gen/go"
)
func TestDevfileState_AddExecCommand(t *testing.T) {
@@ -36,6 +37,7 @@ func TestDevfileState_AddExecCommand(t *testing.T) {
"2Gi",
"100m",
"200m",
nil,
)
if err != nil {
t.Fatal(err)
@@ -96,10 +98,12 @@ schemaVersion: 2.2.0
MemoryLimit: "2Gi",
CpuRequest: "100m",
CpuLimit: "200m",
VolumeMounts: []openapi.VolumeMount{},
},
},
Images: []Image{},
Resources: []Resource{},
Volumes: []Volume{},
Events: Events{},
},
},
@@ -186,6 +190,7 @@ schemaVersion: 2.2.0
},
},
Resources: []Resource{},
Volumes: []Volume{},
Events: Events{},
},
},
@@ -235,6 +240,7 @@ func TestDevfileState_AddCompositeCommand(t *testing.T) {
"2Gi",
"100m",
"200m",
nil,
)
if err != nil {
t.Fatal(err)
@@ -313,10 +319,12 @@ schemaVersion: 2.2.0
MemoryLimit: "2Gi",
CpuRequest: "100m",
CpuLimit: "200m",
VolumeMounts: []openapi.VolumeMount{},
},
},
Images: []Image{},
Resources: []Resource{},
Volumes: []Volume{},
Events: Events{},
},
},
@@ -364,6 +372,7 @@ func TestDevfileState_DeleteCommand(t *testing.T) {
"2Gi",
"100m",
"200m",
nil,
)
if err != nil {
t.Fatal(err)
@@ -412,10 +421,12 @@ schemaVersion: 2.2.0
MemoryLimit: "2Gi",
CpuRequest: "100m",
CpuLimit: "200m",
VolumeMounts: []openapi.VolumeMount{},
},
},
Images: []Image{},
Resources: []Resource{},
Volumes: []Volume{},
Events: Events{},
},
},
@@ -627,6 +638,7 @@ schemaVersion: 2.2.0
Containers: []Container{},
Images: []Image{},
Resources: []Resource{},
Volumes: []Volume{},
},
},
// TODO: Add test cases.
@@ -713,6 +725,7 @@ schemaVersion: 2.2.0
Containers: []Container{},
Images: []Image{},
Resources: []Resource{},
Volumes: []Volume{},
},
},
// TODO: Add test cases.
@@ -801,6 +814,7 @@ schemaVersion: 2.2.0
Containers: []Container{},
Images: []Image{},
Resources: []Resource{},
Volumes: []Volume{},
},
},
// TODO: Add test cases.

View File

@@ -9,7 +9,24 @@ import (
. "github.com/redhat-developer/odo/pkg/apiserver-gen/go"
)
func (o *DevfileState) AddContainer(name string, image string, command []string, args []string, memRequest string, memLimit string, cpuRequest string, cpuLimit string) (DevfileContent, error) {
func (o *DevfileState) AddContainer(
name string,
image string,
command []string,
args []string,
memRequest string,
memLimit string,
cpuRequest string,
cpuLimit string,
volumeMounts []VolumeMount,
) (DevfileContent, error) {
v1alpha2VolumeMounts := make([]v1alpha2.VolumeMount, 0, len(volumeMounts))
for _, vm := range volumeMounts {
v1alpha2VolumeMounts = append(v1alpha2VolumeMounts, v1alpha2.VolumeMount{
Name: vm.Name,
Path: vm.Path,
})
}
container := v1alpha2.Component{
Name: name,
ComponentUnion: v1alpha2.ComponentUnion{
@@ -22,6 +39,7 @@ func (o *DevfileState) AddContainer(name string, image string, command []string,
MemoryLimit: memLimit,
CpuRequest: cpuRequest,
CpuLimit: cpuLimit,
VolumeMounts: v1alpha2VolumeMounts,
},
},
},
@@ -184,3 +202,56 @@ func (o *DevfileState) checkResourceUsed(name string) error {
}
return nil
}
func (o *DevfileState) AddVolume(name string, ephemeral bool, size string) (DevfileContent, error) {
volume := v1alpha2.Component{
Name: name,
ComponentUnion: v1alpha2.ComponentUnion{
Volume: &v1alpha2.VolumeComponent{
Volume: v1alpha2.Volume{
Ephemeral: &ephemeral,
Size: size,
},
},
},
}
err := o.Devfile.Data.AddComponents([]v1alpha2.Component{volume})
if err != nil {
return DevfileContent{}, err
}
return o.GetContent()
}
func (o *DevfileState) DeleteVolume(name string) (DevfileContent, error) {
err := o.checkVolumeUsed(name)
if err != nil {
return DevfileContent{}, fmt.Errorf("error deleting volume %q: %w", name, err)
}
// TODO check if it is a Volume, not another component
err = o.Devfile.Data.DeleteComponent(name)
if err != nil {
return DevfileContent{}, err
}
return o.GetContent()
}
func (o *DevfileState) checkVolumeUsed(name string) error {
containers, err := o.Devfile.Data.GetComponents(common.DevfileOptions{
ComponentOptions: common.ComponentOptions{
ComponentType: v1alpha2.ContainerComponentType,
},
})
if err != nil {
return err
}
for _, container := range containers {
for _, mount := range container.Container.VolumeMounts {
if mount.Name == name {
return fmt.Errorf("volume %q is mounted by Container %q", name, container.Name)
}
}
}
return nil
}

View File

@@ -5,18 +5,20 @@ import (
"github.com/google/go-cmp/cmp"
. "github.com/redhat-developer/odo/pkg/apiserver-gen/go"
openapi "github.com/redhat-developer/odo/pkg/apiserver-gen/go"
)
func TestDevfileState_AddContainer(t *testing.T) {
type args struct {
name string
image string
command []string
args []string
memRequest string
memLimit string
cpuRequest string
cpuLimit string
name string
image string
command []string
args []string
memRequest string
memLimit string
cpuRequest string
cpuLimit string
volumeMounts []openapi.VolumeMount
}
tests := []struct {
name string
@@ -39,6 +41,12 @@ func TestDevfileState_AddContainer(t *testing.T) {
memLimit: "2Gi",
cpuRequest: "100m",
cpuLimit: "200m",
volumeMounts: []openapi.VolumeMount{
{
Name: "vol1",
Path: "/mnt/volume1",
},
},
},
want: DevfileContent{
Content: `components:
@@ -54,6 +62,9 @@ func TestDevfileState_AddContainer(t *testing.T) {
image: an-image
memoryLimit: 2Gi
memoryRequest: 1Gi
volumeMounts:
- name: vol1
path: /mnt/volume1
name: a-name
metadata: {}
schemaVersion: 2.2.0
@@ -69,10 +80,17 @@ schemaVersion: 2.2.0
MemoryLimit: "2Gi",
CpuRequest: "100m",
CpuLimit: "200m",
VolumeMounts: []openapi.VolumeMount{
{
Name: "vol1",
Path: "/mnt/volume1",
},
},
},
},
Images: []Image{},
Resources: []Resource{},
Volumes: []Volume{},
Events: Events{},
},
},
@@ -81,7 +99,7 @@ schemaVersion: 2.2.0
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
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)
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)
if (err != nil) != tt.wantErr {
t.Errorf("DevfileState.AddContainer() error = %v, wantErr %v", err, tt.wantErr)
return
@@ -120,6 +138,7 @@ func TestDevfileState_DeleteContainer(t *testing.T) {
"2Gi",
"100m",
"200m",
nil,
)
if err != nil {
t.Fatal(err)
@@ -137,6 +156,7 @@ schemaVersion: 2.2.0
Containers: []Container{},
Images: []Image{},
Resources: []Resource{},
Volumes: []Volume{},
Events: Events{},
},
},
@@ -153,6 +173,7 @@ schemaVersion: 2.2.0
"2Gi",
"100m",
"200m",
nil,
)
if err != nil {
t.Fatal(err)
@@ -242,6 +263,7 @@ schemaVersion: 2.2.0
},
},
Resources: []Resource{},
Volumes: []Volume{},
Events: Events{},
},
},
@@ -304,6 +326,7 @@ schemaVersion: 2.2.0
Containers: []Container{},
Images: []Image{},
Resources: []Resource{},
Volumes: []Volume{},
Events: Events{},
},
},
@@ -389,7 +412,8 @@ schemaVersion: 2.2.0
Uri: "an-uri",
},
},
Events: Events{},
Volumes: []Volume{},
Events: Events{},
},
},
{
@@ -418,7 +442,8 @@ schemaVersion: 2.2.0
Inlined: "inline resource...",
},
},
Events: Events{},
Volumes: []Volume{},
Events: Events{},
},
},
// TODO: Add test cases.
@@ -477,6 +502,7 @@ schemaVersion: 2.2.0
Containers: []Container{},
Images: []Image{},
Resources: []Resource{},
Volumes: []Volume{},
Events: Events{},
},
},
@@ -519,3 +545,148 @@ schemaVersion: 2.2.0
})
}
}
func TestDevfileState_AddVolume(t *testing.T) {
type args struct {
name string
size string
ephemeral bool
}
tests := []struct {
name string
state func() DevfileState
args args
want DevfileContent
wantErr bool
}{
{
name: "Add a volume",
state: func() DevfileState {
return NewDevfileState()
},
args: args{
name: "a-name",
size: "1Gi",
ephemeral: true,
},
want: DevfileContent{
Content: `components:
- name: a-name
volume:
ephemeral: true
size: 1Gi
metadata: {}
schemaVersion: 2.2.0
`,
Commands: []Command{},
Containers: []Container{},
Images: []Image{},
Resources: []Resource{},
Volumes: []Volume{
{
Name: "a-name",
Size: "1Gi",
Ephemeral: true,
},
},
Events: Events{},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
o := tt.state()
got, err := o.AddVolume(tt.args.name, tt.args.ephemeral, tt.args.size)
if (err != nil) != tt.wantErr {
t.Errorf("DevfileState.AddVolume() error = %v, wantErr %v", err, tt.wantErr)
return
}
if diff := cmp.Diff(tt.want.Content, got.Content); diff != "" {
t.Errorf("DevfileState.AddVolume() mismatch (-want +got):\n%s", diff)
}
if diff := cmp.Diff(tt.want, got); diff != "" {
t.Errorf("DevfileState.AddVolume() mismatch (-want +got):\n%s", diff)
}
})
}
}
func TestDevfileState_DeleteVolume(t *testing.T) {
type args struct {
name string
}
tests := []struct {
name string
state func(t *testing.T) DevfileState
args args
want DevfileContent
wantErr bool
}{
{
name: "Delete an existing volume",
state: func(t *testing.T) DevfileState {
state := NewDevfileState()
_, err := state.AddVolume(
"a-name",
true,
"1Gi",
)
if err != nil {
t.Fatal(err)
}
return state
},
args: args{
name: "a-name",
},
want: DevfileContent{
Content: `metadata: {}
schemaVersion: 2.2.0
`,
Commands: []Command{},
Containers: []Container{},
Images: []Image{},
Resources: []Resource{},
Volumes: []Volume{},
Events: Events{},
},
},
{
name: "Delete a non existing resource",
state: func(t *testing.T) DevfileState {
state := NewDevfileState()
_, err := state.AddVolume(
"a-name",
true,
"1Gi",
)
if err != nil {
t.Fatal(err)
}
return state
},
args: args{
name: "another-name",
},
want: DevfileContent{},
wantErr: true,
},
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
o := tt.state(t)
got, err := o.DeleteVolume(tt.args.name)
if (err != nil) != tt.wantErr {
t.Errorf("DevfileState.DeleteVolume() error = %v, wantErr %v", err, tt.wantErr)
return
}
if diff := cmp.Diff(tt.want.Content, got.Content); diff != "" {
t.Errorf("DevfileState.DeleteVolume() mismatch (-want +got):\n%s", diff)
}
if diff := cmp.Diff(tt.want, got); diff != "" {
t.Errorf("DevfileState.DeleteVolume() mismatch (-want +got):\n%s", diff)
}
})
}
}

View File

@@ -16,97 +16,6 @@ const (
SEPARATOR = ","
)
/*
type DevfileContent struct {
Content string `json:"content"`
Commands []Command `json:"commands"`
Containers []Container `json:"containers"`
Images []Image `json:"images"`
Resources []Resource `json:"resources"`
Events Events `json:"events"`
Metadata Metadata `json:"metadata"`
}
type Metadata struct {
Name string `json:"name"`
Version string `json:"version"`
DisplayName string `json:"displayName"`
Description string `json:"description"`
Tags string `json:"tags"`
Architectures string `json:"architectures"`
Icon string `json:"icon"`
GlobalMemoryLimit string `json:"globalMemoryLimit"`
ProjectType string `json:"projectType"`
Language string `json:"language"`
Website string `json:"website"`
Provider string `json:"provider"`
SupportUrl string `json:"supportUrl"`
}
type Command struct {
Name string `json:"name"`
Group string `json:"group"`
Default bool `json:"default"`
Type string `json:"type"`
Exec *ExecCommand `json:"exec"`
Apply *ApplyCommand `json:"apply"`
Image *ImageCommand `json:"image"`
Composite *CompositeCommand `json:"composite"`
}
type ExecCommand struct {
Component string `json:"component"`
CommandLine string `json:"commandLine"`
WorkingDir string `json:"workingDir"`
HotReloadCapable bool `json:"hotReloadCapable"`
}
type ApplyCommand struct {
Component string `json:"component"`
}
type ImageCommand struct {
Component string `json:"component"`
}
type CompositeCommand struct {
Commands []string `json:"commands"`
Parallel bool `json:"parallel"`
}
type Container struct {
Name string `json:"name"`
Image string `json:"image"`
Command []string `json:"command"`
Args []string `json:"args"`
MemoryRequest string `json:"memoryRequest"`
MemoryLimit string `json:"memoryLimit"`
CpuRequest string `json:"cpuRequest"`
CpuLimit string `json:"cpuLimit"`
}
type Image struct {
Name string `json:"name"`
ImageName string `json:"imageName"`
Args []string `json:"args"`
BuildContext string `json:"buildContext"`
RootRequired bool `json:"rootRequired"`
URI string `json:"uri"`
}
type Resource struct {
Name string `json:"name"`
Inlined string `json:"inlined"`
URI string `json:"uri"`
}
type Events struct {
PreStart []string `json:"preStart"`
PostStart []string `json:"postStart"`
PreStop []string `json:"preStop"`
PostStop []string `json:"postStop"`
}
*/
// getContent returns the YAML content of the global devfile as string
func (o *DevfileState) GetContent() (DevfileContent, error) {
err := o.Devfile.WriteYamlDevfile()
@@ -137,12 +46,18 @@ func (o *DevfileState) GetContent() (DevfileContent, error) {
return DevfileContent{}, errors.New("error getting Kubernetes resources")
}
volumes, err := o.getVolumes()
if err != nil {
return DevfileContent{}, errors.New("error getting volumes")
}
return DevfileContent{
Content: string(result),
Commands: commands,
Containers: containers,
Images: images,
Resources: resources,
Volumes: volumes,
Events: o.getEvents(),
Metadata: o.getMetadata(),
}, nil
@@ -255,11 +170,23 @@ func (o *DevfileState) getContainers() ([]Container, error) {
MemoryLimit: container.ComponentUnion.Container.MemoryLimit,
CpuRequest: container.ComponentUnion.Container.CpuRequest,
CpuLimit: container.ComponentUnion.Container.CpuLimit,
VolumeMounts: o.getVolumeMounts(container.Container.Container),
})
}
return result, nil
}
func (o *DevfileState) getVolumeMounts(container v1alpha2.Container) []VolumeMount {
result := make([]VolumeMount, 0, len(container.VolumeMounts))
for _, vm := range container.VolumeMounts {
result = append(result, VolumeMount{
Name: vm.Name,
Path: vm.Path,
})
}
return result
}
func (o *DevfileState) getImages() ([]Image, error) {
images, err := o.Devfile.Data.GetComponents(common.DevfileOptions{
ComponentOptions: common.ComponentOptions{
@@ -303,6 +230,26 @@ func (o *DevfileState) getResources() ([]Resource, error) {
return result, nil
}
func (o *DevfileState) getVolumes() ([]Volume, error) {
volumes, err := o.Devfile.Data.GetComponents(common.DevfileOptions{
ComponentOptions: common.ComponentOptions{
ComponentType: v1alpha2.VolumeComponentType,
},
})
if err != nil {
return nil, err
}
result := make([]Volume, 0, len(volumes))
for _, volume := range volumes {
result = append(result, Volume{
Name: volume.Name,
Ephemeral: *volume.Volume.Ephemeral,
Size: volume.Volume.Size,
})
}
return result, nil
}
func (o *DevfileState) getEvents() Events {
events := o.Devfile.Data.GetEvents()
return Events{

View File

@@ -24,6 +24,7 @@ func TestDevfileState_GetContent(t *testing.T) {
Containers: []Container{},
Images: []Image{},
Resources: []Resource{},
Volumes: []Volume{},
Events: Events{},
},
},

View File

@@ -39,6 +39,7 @@ schemaVersion: 2.2.0
Containers: []Container{},
Images: []Image{},
Resources: []Resource{},
Volumes: []Volume{},
Events: Events{
PreStart: []string{"command1"},
},
@@ -70,6 +71,7 @@ schemaVersion: 2.2.0
Containers: []Container{},
Images: []Image{},
Resources: []Resource{},
Volumes: []Volume{},
Events: Events{
PreStart: []string{"command1"},
PostStart: []string{"command2"},

View File

@@ -75,6 +75,7 @@ schemaVersion: 2.2.0
Containers: []Container{},
Images: []Image{},
Resources: []Resource{},
Volumes: []Volume{},
Metadata: Metadata{
Name: "a-name",
Version: "v1.1.1",

View File

@@ -543,6 +543,11 @@ paths:
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'
responses:
'200':
description: container was successfully added to the devfile
@@ -813,6 +818,101 @@ paths:
example:
message: "Error deleting the resource"
/devstate/volume:
post:
tags:
- devstate
description: Add a new Volume to the Devfile
requestBody:
content:
application/json:
schema:
type: object
properties:
name:
description: Name of the volume
type: string
size:
description: Minimal size of the volume
type: string
ephemeral:
description: True if the Volume is Ephemeral
type: boolean
responses:
'200':
description: volume was successfully added to the devfile
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 adding the volume
content:
application/json:
schema:
$ref: '#/components/schemas/GeneralError'
example:
message: "Error adding the volume"
/devstate/volume/{volumeName}:
delete:
tags:
- devstate
description: "Delete a volume from the Devfile"
parameters:
- name: volumeName
in: path
description: Volume name to delete
required: true
schema:
type: string
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/GeneralSuccess'
example:
message: "Volume has been deleted"
description: "Volume has been deleted"
'500':
description: Error deleting the volume
content:
application/json:
schema:
$ref: '#/components/schemas/GeneralError'
example:
message: "Error deleting the volume"
/devstate/applyCommand:
post:
tags:
@@ -1315,6 +1415,7 @@ components:
- containers
- images
- resources
- volumes
- events
- metadata
properties:
@@ -1336,6 +1437,10 @@ components:
type: array
items:
$ref: '#/components/schemas/Resource'
volumes:
type: array
items:
$ref: '#/components/schemas/Volume'
events:
$ref: '#/components/schemas/Events'
metadata:
@@ -1416,6 +1521,7 @@ components:
- memoryLimit
- cpuRequest
- cpuLimit
- volumeMounts
properties:
name:
type: string
@@ -1437,6 +1543,20 @@ components:
type: string
cpuLimit:
type: string
volumeMounts:
type: array
items:
$ref: '#/components/schemas/VolumeMount'
VolumeMount:
type: object
required:
- name
- path
properties:
name:
type: string
path:
type: string
Image:
type: object
required:
@@ -1472,6 +1592,17 @@ components:
type: string
uri:
type: string
Volume:
type: object
required:
- name
properties:
name:
type: string
ephemeral:
type: boolean
size:
type: string
Events:
type: object
properties:

View File

@@ -11,6 +11,6 @@
<body class="mat-typography">
<div id="loading">Loading, please wait...</div>
<app-root></app-root>
<script src="runtime.1289ea0acffcdc5e.js" type="module"></script><script src="polyfills.8b3b37cedaf377c3.js" type="module"></script><script src="main.ae49ed4fe0fa0670.js" type="module"></script>
<script src="runtime.1289ea0acffcdc5e.js" type="module"></script><script src="polyfills.8b3b37cedaf377c3.js" type="module"></script><script src="main.1046d99cec4375b1.js" type="module"></script>
</body></html>

File diff suppressed because one or more lines are too long