...

Source file src/github.com/redhat-developer/odo/pkg/apiserver-impl/devstate/components.go

Documentation: github.com/redhat-developer/odo/pkg/apiserver-impl/devstate

     1  package devstate
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
     8  	"github.com/devfile/library/v2/pkg/devfile/parser/data/v2/common"
     9  	. "github.com/redhat-developer/odo/pkg/apiserver-gen/go"
    10  	"k8s.io/utils/pointer"
    11  )
    12  
    13  func (o *DevfileState) AddContainer(
    14  	name string,
    15  	image string,
    16  	command []string,
    17  	args []string,
    18  	envs []Env,
    19  	memRequest string,
    20  	memLimit string,
    21  	cpuRequest string,
    22  	cpuLimit string,
    23  	volumeMounts []VolumeMount,
    24  	configureSources bool,
    25  	mountSources bool,
    26  	sourceMapping string,
    27  	annotation Annotation,
    28  	endpoints []Endpoint,
    29  ) (DevfileContent, error) {
    30  
    31  	container := v1alpha2.Component{
    32  		Name: name,
    33  		ComponentUnion: v1alpha2.ComponentUnion{
    34  			Container: &v1alpha2.ContainerComponent{
    35  				Container: v1alpha2.Container{
    36  					Image:         image,
    37  					Command:       command,
    38  					Args:          args,
    39  					Env:           tov1alpha2EnvVars(envs),
    40  					MemoryRequest: memRequest,
    41  					MemoryLimit:   memLimit,
    42  					CpuRequest:    cpuRequest,
    43  					CpuLimit:      cpuLimit,
    44  					VolumeMounts:  tov1alpha2VolumeMounts(volumeMounts),
    45  					Annotation:    tov1alpha2Annotation(annotation),
    46  				},
    47  				Endpoints: tov1alpha2Endpoints(endpoints),
    48  			},
    49  		},
    50  	}
    51  	if configureSources {
    52  		container.Container.MountSources = &mountSources
    53  		container.Container.SourceMapping = sourceMapping
    54  	}
    55  	err := o.Devfile.Data.AddComponents([]v1alpha2.Component{container})
    56  	if err != nil {
    57  		return DevfileContent{}, err
    58  	}
    59  	return o.GetContent()
    60  }
    61  
    62  func (o *DevfileState) PatchContainer(
    63  	name string,
    64  	image string,
    65  	command []string,
    66  	args []string,
    67  	envs []Env,
    68  	memRequest string,
    69  	memLimit string,
    70  	cpuRequest string,
    71  	cpuLimit string,
    72  	volumeMounts []VolumeMount,
    73  	configureSources bool,
    74  	mountSources bool,
    75  	sourceMapping string,
    76  	annotation Annotation,
    77  	endpoints []Endpoint,
    78  ) (DevfileContent, error) {
    79  	found, err := o.Devfile.Data.GetComponents(common.DevfileOptions{
    80  		ComponentOptions: common.ComponentOptions{
    81  			ComponentType: v1alpha2.ContainerComponentType,
    82  		},
    83  		FilterByName: name,
    84  	})
    85  	if err != nil {
    86  		return DevfileContent{}, err
    87  	}
    88  	if len(found) != 1 {
    89  		return DevfileContent{}, fmt.Errorf("%d Container found with name %q", len(found), name)
    90  	}
    91  
    92  	container := found[0]
    93  	container.Container.Image = image
    94  	container.Container.Command = command
    95  	container.Container.Args = args
    96  	container.Container.Env = tov1alpha2EnvVars(envs)
    97  	container.Container.MemoryRequest = memRequest
    98  	container.Container.MemoryLimit = memLimit
    99  	container.Container.CpuRequest = cpuRequest
   100  	container.Container.CpuLimit = cpuLimit
   101  	container.Container.VolumeMounts = tov1alpha2VolumeMounts(volumeMounts)
   102  
   103  	container.Container.MountSources = nil
   104  	container.Container.SourceMapping = ""
   105  	if configureSources {
   106  		container.Container.MountSources = &mountSources
   107  		container.Container.SourceMapping = sourceMapping
   108  	}
   109  	container.Container.Annotation = tov1alpha2Annotation(annotation)
   110  	container.Container.Endpoints = tov1alpha2Endpoints(endpoints)
   111  
   112  	err = o.Devfile.Data.UpdateComponent(container)
   113  	if err != nil {
   114  		return DevfileContent{}, err
   115  	}
   116  	return o.GetContent()
   117  }
   118  
   119  func tov1alpha2EnvVars(envs []Env) []v1alpha2.EnvVar {
   120  	result := make([]v1alpha2.EnvVar, 0, len(envs))
   121  	for _, env := range envs {
   122  		result = append(result, v1alpha2.EnvVar{
   123  			Name:  env.Name,
   124  			Value: env.Value,
   125  		})
   126  	}
   127  	return result
   128  }
   129  
   130  func tov1alpha2VolumeMounts(volumeMounts []VolumeMount) []v1alpha2.VolumeMount {
   131  	result := make([]v1alpha2.VolumeMount, 0, len(volumeMounts))
   132  	for _, vm := range volumeMounts {
   133  		result = append(result, v1alpha2.VolumeMount{
   134  			Name: vm.Name,
   135  			Path: vm.Path,
   136  		})
   137  	}
   138  	return result
   139  }
   140  
   141  func tov1alpha2Annotation(annotation Annotation) *v1alpha2.Annotation {
   142  	var result *v1alpha2.Annotation
   143  	if len(annotation.Deployment) > 0 || len(annotation.Service) > 0 {
   144  		result = &v1alpha2.Annotation{}
   145  		if len(annotation.Deployment) > 0 {
   146  			result.Deployment = annotation.Deployment
   147  		}
   148  		if len(annotation.Service) > 0 {
   149  			result.Service = annotation.Service
   150  		}
   151  	}
   152  	return result
   153  }
   154  
   155  func tov1alpha2Endpoints(endpoints []Endpoint) []v1alpha2.Endpoint {
   156  	result := make([]v1alpha2.Endpoint, 0, len(endpoints))
   157  	for _, endpoint := range endpoints {
   158  		endpoint := endpoint
   159  		result = append(result, v1alpha2.Endpoint{
   160  			Name:       endpoint.Name,
   161  			TargetPort: int(endpoint.TargetPort),
   162  			Exposure:   v1alpha2.EndpointExposure(endpoint.Exposure),
   163  			Protocol:   v1alpha2.EndpointProtocol(endpoint.Protocol),
   164  			Secure:     &endpoint.Secure,
   165  			Path:       endpoint.Path,
   166  		})
   167  	}
   168  	return result
   169  }
   170  
   171  func (o *DevfileState) DeleteContainer(name string) (DevfileContent, error) {
   172  
   173  	err := o.checkContainerUsed(name)
   174  	if err != nil {
   175  		return DevfileContent{}, fmt.Errorf("error deleting container %q: %w", name, err)
   176  	}
   177  
   178  	// TODO check if it is a Container, not another component
   179  
   180  	err = o.Devfile.Data.DeleteComponent(name)
   181  	if err != nil {
   182  		return DevfileContent{}, err
   183  	}
   184  	return o.GetContent()
   185  }
   186  
   187  func (o *DevfileState) checkContainerUsed(name string) error {
   188  	commands, err := o.Devfile.Data.GetCommands(common.DevfileOptions{
   189  		CommandOptions: common.CommandOptions{
   190  			CommandType: v1alpha2.ExecCommandType,
   191  		},
   192  	})
   193  	if err != nil {
   194  		return err
   195  	}
   196  	for _, command := range commands {
   197  		if command.Exec.Component == name {
   198  			return fmt.Errorf("container %q is used by exec command %q", name, command.Id)
   199  		}
   200  	}
   201  	return nil
   202  }
   203  
   204  func (o *DevfileState) AddImage(name string, imageName string, args []string, buildContext string, rootRequired bool, uri string, autoBuild string) (DevfileContent, error) {
   205  	container := v1alpha2.Component{
   206  		Name: name,
   207  		ComponentUnion: v1alpha2.ComponentUnion{
   208  			Image: &v1alpha2.ImageComponent{
   209  				Image: v1alpha2.Image{
   210  					ImageName: imageName,
   211  					ImageUnion: v1alpha2.ImageUnion{
   212  						Dockerfile: &v1alpha2.DockerfileImage{
   213  							Dockerfile: v1alpha2.Dockerfile{
   214  								Args:         args,
   215  								BuildContext: buildContext,
   216  								RootRequired: &rootRequired,
   217  							},
   218  							DockerfileSrc: v1alpha2.DockerfileSrc{
   219  								Uri: uri,
   220  							},
   221  						},
   222  					},
   223  				},
   224  			},
   225  		},
   226  	}
   227  	if autoBuild == "never" {
   228  		container.Image.AutoBuild = pointer.Bool(false)
   229  	} else if autoBuild == "always" {
   230  		container.Image.AutoBuild = pointer.Bool(true)
   231  	}
   232  	err := o.Devfile.Data.AddComponents([]v1alpha2.Component{container})
   233  	if err != nil {
   234  		return DevfileContent{}, err
   235  	}
   236  	return o.GetContent()
   237  }
   238  
   239  func (o *DevfileState) PatchImage(name string, imageName string, args []string, buildContext string, rootRequired bool, uri string, autoBuild string) (DevfileContent, error) {
   240  	found, err := o.Devfile.Data.GetComponents(common.DevfileOptions{
   241  		ComponentOptions: common.ComponentOptions{
   242  			ComponentType: v1alpha2.ImageComponentType,
   243  		},
   244  		FilterByName: name,
   245  	})
   246  	if err != nil {
   247  		return DevfileContent{}, err
   248  	}
   249  	if len(found) != 1 {
   250  		return DevfileContent{}, fmt.Errorf("%d Image found with name %q", len(found), name)
   251  	}
   252  
   253  	image := found[0]
   254  	if image.Image == nil {
   255  		image.Image = &v1alpha2.ImageComponent{}
   256  	}
   257  	image.Image.ImageName = imageName
   258  	if image.Image.Dockerfile == nil {
   259  		image.Image.Dockerfile = &v1alpha2.DockerfileImage{}
   260  	}
   261  	image.Image.Dockerfile.Args = args
   262  	image.Image.Dockerfile.BuildContext = buildContext
   263  	image.Image.Dockerfile.RootRequired = &rootRequired
   264  	image.Image.Dockerfile.DockerfileSrc.Uri = uri
   265  	image.Image.AutoBuild = nil
   266  	if autoBuild == "never" {
   267  		image.Image.AutoBuild = pointer.Bool(false)
   268  	} else if autoBuild == "always" {
   269  		image.Image.AutoBuild = pointer.Bool(true)
   270  	}
   271  	err = o.Devfile.Data.UpdateComponent(image)
   272  	if err != nil {
   273  		return DevfileContent{}, err
   274  	}
   275  	return o.GetContent()
   276  }
   277  
   278  func (o *DevfileState) DeleteImage(name string) (DevfileContent, error) {
   279  
   280  	err := o.checkImageUsed(name)
   281  	if err != nil {
   282  		return DevfileContent{}, fmt.Errorf("error deleting image %q: %w", name, err)
   283  	}
   284  
   285  	// TODO check if it is an Image, not another component
   286  
   287  	err = o.Devfile.Data.DeleteComponent(name)
   288  	if err != nil {
   289  		return DevfileContent{}, err
   290  	}
   291  	return o.GetContent()
   292  }
   293  
   294  func (o *DevfileState) checkImageUsed(name string) error {
   295  	commands, err := o.Devfile.Data.GetCommands(common.DevfileOptions{
   296  		CommandOptions: common.CommandOptions{
   297  			CommandType: v1alpha2.ApplyCommandType,
   298  		},
   299  	})
   300  	if err != nil {
   301  		return err
   302  	}
   303  	for _, command := range commands {
   304  		if command.Apply.Component == name {
   305  			return fmt.Errorf("image %q is used by Image Command %q", name, command.Id)
   306  		}
   307  	}
   308  	return nil
   309  }
   310  
   311  func (o *DevfileState) AddResource(name string, inlined string, uri string, deployByDefault string) (DevfileContent, error) {
   312  	if inlined != "" && uri != "" {
   313  		return DevfileContent{}, errors.New("both inlined and uri cannot be set at the same time")
   314  	}
   315  	container := v1alpha2.Component{
   316  		Name: name,
   317  		ComponentUnion: v1alpha2.ComponentUnion{
   318  			Kubernetes: &v1alpha2.KubernetesComponent{
   319  				K8sLikeComponent: v1alpha2.K8sLikeComponent{
   320  					K8sLikeComponentLocation: v1alpha2.K8sLikeComponentLocation{
   321  						Inlined: inlined,
   322  						Uri:     uri,
   323  					},
   324  				},
   325  			},
   326  		},
   327  	}
   328  	if deployByDefault == "never" {
   329  		container.Kubernetes.DeployByDefault = pointer.Bool(false)
   330  	} else if deployByDefault == "always" {
   331  		container.Kubernetes.DeployByDefault = pointer.Bool(true)
   332  	}
   333  
   334  	err := o.Devfile.Data.AddComponents([]v1alpha2.Component{container})
   335  	if err != nil {
   336  		return DevfileContent{}, err
   337  	}
   338  	return o.GetContent()
   339  }
   340  
   341  func (o *DevfileState) PatchResource(name string, inlined string, uri string, deployByDefault string) (DevfileContent, error) {
   342  	if inlined != "" && uri != "" {
   343  		return DevfileContent{}, errors.New("both inlined and uri cannot be set at the same time")
   344  	}
   345  	found, err := o.Devfile.Data.GetComponents(common.DevfileOptions{
   346  		ComponentOptions: common.ComponentOptions{
   347  			ComponentType: v1alpha2.KubernetesComponentType,
   348  		},
   349  		FilterByName: name,
   350  	})
   351  	if err != nil {
   352  		return DevfileContent{}, err
   353  	}
   354  	if len(found) != 1 {
   355  		return DevfileContent{}, fmt.Errorf("%d Resource found with name %q", len(found), name)
   356  	}
   357  
   358  	resource := found[0]
   359  	resource.Kubernetes.Inlined = inlined
   360  	resource.Kubernetes.Uri = uri
   361  	resource.Kubernetes.DeployByDefault = nil
   362  	if deployByDefault == "never" {
   363  		resource.Kubernetes.DeployByDefault = pointer.Bool(false)
   364  	} else if deployByDefault == "always" {
   365  		resource.Kubernetes.DeployByDefault = pointer.Bool(true)
   366  	}
   367  
   368  	err = o.Devfile.Data.UpdateComponent(resource)
   369  	if err != nil {
   370  		return DevfileContent{}, err
   371  	}
   372  
   373  	return o.GetContent()
   374  }
   375  
   376  func (o *DevfileState) DeleteResource(name string) (DevfileContent, error) {
   377  
   378  	err := o.checkResourceUsed(name)
   379  	if err != nil {
   380  		return DevfileContent{}, fmt.Errorf("error deleting resource %q: %w", name, err)
   381  	}
   382  	// TODO check if it is a Resource, not another component
   383  
   384  	err = o.Devfile.Data.DeleteComponent(name)
   385  	if err != nil {
   386  		return DevfileContent{}, err
   387  	}
   388  	return o.GetContent()
   389  }
   390  
   391  func (o *DevfileState) checkResourceUsed(name string) error {
   392  	commands, err := o.Devfile.Data.GetCommands(common.DevfileOptions{
   393  		CommandOptions: common.CommandOptions{
   394  			CommandType: v1alpha2.ApplyCommandType,
   395  		},
   396  	})
   397  	if err != nil {
   398  		return err
   399  	}
   400  	for _, command := range commands {
   401  		if command.Apply.Component == name {
   402  			return fmt.Errorf("resource %q is used by Apply Command %q", name, command.Id)
   403  		}
   404  	}
   405  	return nil
   406  }
   407  
   408  func (o *DevfileState) AddVolume(name string, ephemeral bool, size string) (DevfileContent, error) {
   409  	volume := v1alpha2.Component{
   410  		Name: name,
   411  		ComponentUnion: v1alpha2.ComponentUnion{
   412  			Volume: &v1alpha2.VolumeComponent{
   413  				Volume: v1alpha2.Volume{
   414  					Ephemeral: &ephemeral,
   415  					Size:      size,
   416  				},
   417  			},
   418  		},
   419  	}
   420  	err := o.Devfile.Data.AddComponents([]v1alpha2.Component{volume})
   421  	if err != nil {
   422  		return DevfileContent{}, err
   423  	}
   424  	return o.GetContent()
   425  }
   426  
   427  func (o *DevfileState) PatchVolume(name string, ephemeral bool, size string) (DevfileContent, error) {
   428  	found, err := o.Devfile.Data.GetComponents(common.DevfileOptions{
   429  		ComponentOptions: common.ComponentOptions{
   430  			ComponentType: v1alpha2.VolumeComponentType,
   431  		},
   432  		FilterByName: name,
   433  	})
   434  	if err != nil {
   435  		return DevfileContent{}, err
   436  	}
   437  	if len(found) != 1 {
   438  		return DevfileContent{}, fmt.Errorf("%d Volume found with name %q", len(found), name)
   439  	}
   440  
   441  	volume := found[0]
   442  	volume.Volume.Ephemeral = &ephemeral
   443  	volume.Volume.Size = size
   444  
   445  	err = o.Devfile.Data.UpdateComponent(volume)
   446  	if err != nil {
   447  		return DevfileContent{}, err
   448  	}
   449  	return o.GetContent()
   450  }
   451  
   452  func (o *DevfileState) DeleteVolume(name string) (DevfileContent, error) {
   453  	err := o.checkVolumeUsed(name)
   454  	if err != nil {
   455  		return DevfileContent{}, fmt.Errorf("error deleting volume %q: %w", name, err)
   456  	}
   457  	// TODO check if it is a Volume, not another component
   458  
   459  	err = o.Devfile.Data.DeleteComponent(name)
   460  	if err != nil {
   461  		return DevfileContent{}, err
   462  	}
   463  	return o.GetContent()
   464  }
   465  
   466  func (o *DevfileState) checkVolumeUsed(name string) error {
   467  	containers, err := o.Devfile.Data.GetComponents(common.DevfileOptions{
   468  		ComponentOptions: common.ComponentOptions{
   469  			ComponentType: v1alpha2.ContainerComponentType,
   470  		},
   471  	})
   472  	if err != nil {
   473  		return err
   474  	}
   475  	for _, container := range containers {
   476  		for _, mount := range container.Container.VolumeMounts {
   477  			if mount.Name == name {
   478  				return fmt.Errorf("volume %q is mounted by Container %q", name, container.Name)
   479  			}
   480  		}
   481  	}
   482  	return nil
   483  }
   484  

View as plain text