...

Source file src/github.com/redhat-developer/odo/pkg/storage/kubernetes.go

Documentation: github.com/redhat-developer/odo/pkg/storage

     1  package storage
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/devfile/library/v2/pkg/devfile/generator"
     8  	v1 "k8s.io/api/apps/v1"
     9  	corev1 "k8s.io/api/core/v1"
    10  	"k8s.io/apimachinery/pkg/api/resource"
    11  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    12  	"k8s.io/klog"
    13  
    14  	"github.com/redhat-developer/odo/pkg/kclient"
    15  	odolabels "github.com/redhat-developer/odo/pkg/labels"
    16  )
    17  
    18  // kubernetesClient contains information required for devfile based Storage operations
    19  type kubernetesClient struct {
    20  	generic
    21  	client kclient.ClientInterface
    22  
    23  	// if we don't have access to the local config
    24  	// we can use the deployment to call List() and
    25  	// directly list storage from the cluster without the local config
    26  	deployment *v1.Deployment
    27  }
    28  
    29  var _ Client = (*kubernetesClient)(nil)
    30  
    31  // Create creates a pvc from the given Storage
    32  func (k kubernetesClient) Create(storage Storage) error {
    33  
    34  	if k.componentName == "" || k.appName == "" {
    35  		return fmt.Errorf("the component name and the app name should be provided")
    36  	}
    37  
    38  	pvcName, err := generatePVCName(storage.Name, k.componentName, k.appName)
    39  	if err != nil {
    40  		return err
    41  	}
    42  
    43  	labels := odolabels.GetLabels(k.componentName, k.appName, k.runtime, odolabels.ComponentDevMode, false)
    44  	odolabels.AddStorageInfo(labels, storage.Name, strings.Contains(storage.Name, OdoSourceVolume))
    45  
    46  	objectMeta := generator.GetObjectMeta(pvcName, k.client.GetCurrentNamespace(), labels, nil)
    47  
    48  	quantity, err := resource.ParseQuantity(storage.Spec.Size)
    49  	if err != nil {
    50  		return fmt.Errorf("unable to parse size: %v: %w", storage.Spec.Size, err)
    51  	}
    52  
    53  	pvcParams := generator.PVCParams{
    54  		ObjectMeta: objectMeta,
    55  		Quantity:   quantity,
    56  	}
    57  	pvc := generator.GetPVC(pvcParams)
    58  
    59  	// Create PVC
    60  	klog.V(2).Infof("Creating a PVC with name %v and labels %v", pvcName, labels)
    61  	_, err = k.client.CreatePVC(*pvc)
    62  	if err != nil {
    63  		return fmt.Errorf("unable to create PVC: %w", err)
    64  	}
    65  	return nil
    66  }
    67  
    68  // Delete deletes the pvc belonging to the given Storage
    69  func (k kubernetesClient) Delete(name string) error {
    70  	pvcName, err := getPVCNameFromStorageName(k.client, name)
    71  	if err != nil {
    72  		return err
    73  	}
    74  
    75  	// delete the associated PVC with the component
    76  	err = k.client.DeletePVC(pvcName)
    77  	if err != nil {
    78  		return fmt.Errorf("unable to delete PVC %v: %w", pvcName, err)
    79  	}
    80  
    81  	return nil
    82  }
    83  
    84  // List lists pvc based Storage from the cluster
    85  func (k kubernetesClient) List() (StorageList, error) {
    86  	if k.deployment == nil {
    87  		var err error
    88  		selector := odolabels.GetSelector(k.componentName, k.appName, odolabels.ComponentAnyMode, true)
    89  		k.deployment, err = k.client.GetOneDeploymentFromSelector(selector)
    90  		if err != nil {
    91  			if _, ok := err.(*kclient.DeploymentNotFoundError); ok {
    92  				return StorageList{}, nil
    93  			}
    94  			return StorageList{}, err
    95  		}
    96  	}
    97  
    98  	initContainerVolumeMounts := make(map[string]bool)
    99  	for _, container := range k.deployment.Spec.Template.Spec.InitContainers {
   100  		for _, volumeMount := range container.VolumeMounts {
   101  			initContainerVolumeMounts[volumeMount.Name] = true
   102  		}
   103  	}
   104  
   105  	containerVolumeMounts := make(map[string]bool)
   106  	for _, container := range k.deployment.Spec.Template.Spec.Containers {
   107  		for _, volumeMount := range container.VolumeMounts {
   108  			containerVolumeMounts[volumeMount.Name] = true
   109  		}
   110  	}
   111  
   112  	var storage []Storage
   113  	var volumeMounts []Storage
   114  	for _, container := range k.deployment.Spec.Template.Spec.Containers {
   115  		for _, volumeMount := range container.VolumeMounts {
   116  
   117  			// avoid the volume mounts:
   118  			// - only from the init containers
   119  			// - of source volume
   120  			// - of automounted volumes
   121  			_, initOK := initContainerVolumeMounts[volumeMount.Name]
   122  			_, ok := containerVolumeMounts[volumeMount.Name]
   123  			if (!ok && initOK) ||
   124  				volumeMount.Name == OdoSourceVolume ||
   125  				volumeMount.Name == SharedDataVolumeName ||
   126  				strings.HasPrefix(volumeMount.Name, "auto-") {
   127  				continue
   128  			}
   129  
   130  			volumeMounts = append(volumeMounts, Storage{
   131  				ObjectMeta: metav1.ObjectMeta{Name: volumeMount.Name},
   132  				Spec: StorageSpec{
   133  					Path:          volumeMount.MountPath,
   134  					ContainerName: container.Name,
   135  				},
   136  			})
   137  
   138  		}
   139  	}
   140  
   141  	if len(volumeMounts) <= 0 {
   142  		return StorageList{}, nil
   143  	}
   144  
   145  	selector := odolabels.SelectorBuilder().WithComponent(k.componentName).WithoutSourcePVC(OdoSourceVolume).Selector()
   146  	pvcs, err := k.client.ListPVCs(selector)
   147  	if err != nil {
   148  		return StorageList{}, fmt.Errorf("unable to get PVC using selector %q: %w", selector, err)
   149  	}
   150  
   151  	// to track volume mounts used by a PVC
   152  	validVolumeMounts := make(map[string]bool)
   153  
   154  	for _, pvc := range pvcs {
   155  		found := false
   156  		for _, volumeMount := range volumeMounts {
   157  			if volumeMount.Name == pvc.Name+"-vol" {
   158  				// this volume mount is used by a PVC
   159  				validVolumeMounts[volumeMount.Name] = true
   160  
   161  				found = true
   162  				size := pvc.Spec.Resources.Requests[corev1.ResourceStorage]
   163  				storage = append(storage, NewStorageWithContainer(odolabels.GetDevfileStorageName(pvc.Labels), size.String(), volumeMount.Spec.Path, volumeMount.Spec.ContainerName, nil))
   164  			}
   165  		}
   166  		if !found {
   167  			return StorageList{}, fmt.Errorf("mount path for pvc %s not found", pvc.Name)
   168  		}
   169  	}
   170  
   171  	// to track non PVC volumes
   172  	for _, volume := range k.deployment.Spec.Template.Spec.Volumes {
   173  		if volume.PersistentVolumeClaim == nil {
   174  			validVolumeMounts[volume.Name] = true
   175  		}
   176  	}
   177  
   178  	for _, volumeMount := range volumeMounts {
   179  		if _, ok := validVolumeMounts[volumeMount.Name]; !ok {
   180  			return StorageList{}, fmt.Errorf("pvc not found for mount path %s", volumeMount.Name)
   181  		}
   182  	}
   183  
   184  	return StorageList{Items: storage}, nil
   185  }
   186  

View as plain text