...

Source file src/github.com/redhat-developer/odo/pkg/dev/podmandev/podmandev.go

Documentation: github.com/redhat-developer/odo/pkg/dev/podmandev

     1  package podmandev
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  
     8  	devfilev1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
     9  	"k8s.io/klog"
    10  
    11  	"github.com/redhat-developer/odo/pkg/dev"
    12  	"github.com/redhat-developer/odo/pkg/dev/common"
    13  	"github.com/redhat-developer/odo/pkg/devfile"
    14  	"github.com/redhat-developer/odo/pkg/devfile/location"
    15  	"github.com/redhat-developer/odo/pkg/exec"
    16  	"github.com/redhat-developer/odo/pkg/libdevfile"
    17  	"github.com/redhat-developer/odo/pkg/log"
    18  	odocontext "github.com/redhat-developer/odo/pkg/odo/context"
    19  	"github.com/redhat-developer/odo/pkg/podman"
    20  	"github.com/redhat-developer/odo/pkg/portForward"
    21  	"github.com/redhat-developer/odo/pkg/preference"
    22  	"github.com/redhat-developer/odo/pkg/state"
    23  	"github.com/redhat-developer/odo/pkg/sync"
    24  	"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
    25  	"github.com/redhat-developer/odo/pkg/watch"
    26  
    27  	corev1 "k8s.io/api/core/v1"
    28  )
    29  
    30  type DevClient struct {
    31  	fs filesystem.Filesystem
    32  
    33  	podmanClient      podman.Client
    34  	prefClient        preference.Client
    35  	portForwardClient portForward.Client
    36  	syncClient        sync.Client
    37  	execClient        exec.Client
    38  	stateClient       state.Client
    39  	watchClient       watch.Client
    40  
    41  	deployedPod *corev1.Pod
    42  	usedPorts   []int
    43  }
    44  
    45  var _ dev.Client = (*DevClient)(nil)
    46  
    47  func NewDevClient(
    48  	fs filesystem.Filesystem,
    49  	podmanClient podman.Client,
    50  	prefClient preference.Client,
    51  	portForwardClient portForward.Client,
    52  	syncClient sync.Client,
    53  	execClient exec.Client,
    54  	stateClient state.Client,
    55  	watchClient watch.Client,
    56  ) *DevClient {
    57  	return &DevClient{
    58  		fs:                fs,
    59  		podmanClient:      podmanClient,
    60  		prefClient:        prefClient,
    61  		portForwardClient: portForwardClient,
    62  		syncClient:        syncClient,
    63  		execClient:        execClient,
    64  		stateClient:       stateClient,
    65  		watchClient:       watchClient,
    66  	}
    67  }
    68  
    69  func (o *DevClient) Start(
    70  	ctx context.Context,
    71  	options dev.StartOptions,
    72  ) error {
    73  	klog.V(4).Infoln("Creating new adapter")
    74  
    75  	var (
    76  		componentStatus = watch.ComponentStatus{
    77  			ImageComponentsAutoApplied: make(map[string]devfilev1.ImageComponent),
    78  		}
    79  	)
    80  
    81  	klog.V(4).Infoln("Creating inner-loop resources for the component")
    82  
    83  	watchParameters := watch.WatchParameters{
    84  		StartOptions:        options,
    85  		DevfileWatchHandler: o.watchHandler,
    86  		WatchCluster:        false,
    87  	}
    88  
    89  	return o.watchClient.WatchAndPush(ctx, watchParameters, componentStatus)
    90  }
    91  
    92  // syncFiles syncs the local source files in path into the pod's source volume
    93  func (o *DevClient) syncFiles(ctx context.Context, options dev.StartOptions, pod *corev1.Pod, path string) (bool, error) {
    94  	var (
    95  		devfileObj    = odocontext.GetEffectiveDevfileObj(ctx)
    96  		componentName = odocontext.GetComponentName(ctx)
    97  	)
    98  
    99  	containerName, syncFolder, err := common.GetFirstContainerWithSourceVolume(pod.Spec.Containers)
   100  	if err != nil {
   101  		return false, fmt.Errorf("error while retrieving container from pod %s with a mounted project volume: %w", pod.GetName(), err)
   102  	}
   103  
   104  	compInfo := sync.ComponentInfo{
   105  		ComponentName: componentName,
   106  		ContainerName: containerName,
   107  		PodName:       pod.GetName(),
   108  		SyncFolder:    syncFolder,
   109  	}
   110  	s := log.Spinner("Syncing files into the container")
   111  	defer s.End(false)
   112  
   113  	syncFilesMap := make(map[string]string)
   114  	var devfileCmd devfilev1.Command
   115  	innerLoopWithCommands := !options.SkipCommands
   116  	if innerLoopWithCommands {
   117  		var (
   118  			cmdKind = devfilev1.RunCommandGroupKind
   119  			cmdName = options.RunCommand
   120  		)
   121  		if options.Debug {
   122  			cmdKind = devfilev1.DebugCommandGroupKind
   123  			cmdName = options.DebugCommand
   124  		}
   125  		var hasCmd bool
   126  		devfileCmd, hasCmd, err = libdevfile.GetCommand(*devfileObj, cmdName, cmdKind)
   127  		if err != nil {
   128  			return false, err
   129  		}
   130  		if hasCmd {
   131  			syncFilesMap = common.GetSyncFilesFromAttributes(devfileCmd)
   132  		} else {
   133  			klog.V(2).Infof("no command found with name %q and kind %v, syncing files without command attributes", cmdName, cmdKind)
   134  		}
   135  	}
   136  
   137  	syncParams := sync.SyncParameters{
   138  		Path:                     path,
   139  		WatchFiles:               nil,
   140  		WatchDeletedFiles:        nil,
   141  		IgnoredFiles:             options.IgnorePaths,
   142  		DevfileScanIndexForWatch: true,
   143  
   144  		CompInfo:  compInfo,
   145  		ForcePush: true,
   146  		Files:     syncFilesMap,
   147  	}
   148  	execRequired, err := o.syncClient.SyncFiles(ctx, syncParams)
   149  	if err != nil {
   150  		return false, err
   151  	}
   152  	s.End(true)
   153  	return execRequired, nil
   154  }
   155  
   156  // checkVolumesFree checks that all persistent volumes declared in pod
   157  // are not using an existing volume
   158  func (o *DevClient) checkVolumesFree(pod *corev1.Pod) error {
   159  	existingVolumesSet, err := o.podmanClient.VolumeLs()
   160  	if err != nil {
   161  		return err
   162  	}
   163  	var problematicVolumes []string
   164  	for _, volume := range pod.Spec.Volumes {
   165  		if volume.PersistentVolumeClaim != nil && existingVolumesSet[volume.PersistentVolumeClaim.ClaimName] {
   166  			problematicVolumes = append(problematicVolumes, volume.PersistentVolumeClaim.ClaimName)
   167  		}
   168  	}
   169  	if len(problematicVolumes) > 0 {
   170  		return fmt.Errorf("volumes already exist, please remove them before to run odo dev: %s", strings.Join(problematicVolumes, ", "))
   171  	}
   172  	return nil
   173  }
   174  
   175  func (o *DevClient) watchHandler(ctx context.Context, pushParams common.PushParameters, componentStatus *watch.ComponentStatus) error {
   176  
   177  	devObj, err := devfile.ParseAndValidateFromFileWithVariables(location.DevfileLocation(o.fs, ""), pushParams.StartOptions.Variables, o.prefClient.GetImageRegistry(), true)
   178  	if err != nil {
   179  		return fmt.Errorf("unable to read devfile: %w", err)
   180  	}
   181  	pushParams.Devfile = devObj
   182  
   183  	return o.reconcile(ctx, pushParams, componentStatus)
   184  }
   185  

View as plain text