mirror of
https://github.com/redhat-developer/odo.git
synced 2025-10-19 03:06:19 +03:00
Simplify devfile Kubernetes adapter (#6762)
* Get values from context * Move Devfile param to WatchParams and biuld adapter only once * Move pkg/devfile/adapters/kubernetes/* into pkg/dev/kubedev * Rename Push to reconcile and split in 2 parts: components and innreloop * Pass out ans errout as startOptions * Embed StartOptions into PushParameters * Embed StartOptions into WatchParameters * Fix passing startoptions * Deduplicate options (out, ...) * Revert adding unwanted files * Fix wait app ready
This commit is contained in:
@@ -18,7 +18,7 @@ import (
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/component"
|
||||
"github.com/redhat-developer/odo/pkg/configAutomount"
|
||||
"github.com/redhat-developer/odo/pkg/devfile/adapters/kubernetes/storage"
|
||||
"github.com/redhat-developer/odo/pkg/dev/kubedev/storage"
|
||||
"github.com/redhat-developer/odo/pkg/devfile/image"
|
||||
"github.com/redhat-developer/odo/pkg/kclient"
|
||||
odolabels "github.com/redhat-developer/odo/pkg/labels"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package adapters
|
||||
package common
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
@@ -1,4 +1,4 @@
|
||||
package adapters
|
||||
package common
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -1,4 +1,4 @@
|
||||
package adapters
|
||||
package common
|
||||
|
||||
import "fmt"
|
||||
|
||||
17
pkg/dev/common/types.go
Normal file
17
pkg/dev/common/types.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/devfile/library/v2/pkg/devfile/parser"
|
||||
"github.com/redhat-developer/odo/pkg/dev"
|
||||
)
|
||||
|
||||
// PushParameters is a struct containing the parameters to be used when pushing to a devfile component
|
||||
type PushParameters struct {
|
||||
StartOptions dev.StartOptions
|
||||
|
||||
Devfile parser.DevfileObj
|
||||
WatchFiles []string // Optional: WatchFiles is the list of changed files detected by odo watch. If empty or nil, odo will check .odo/odo-file-index.json to determine changed files
|
||||
WatchDeletedFiles []string // Optional: WatchDeletedFiles is the list of deleted files detected by odo watch. If empty or nil, odo will check .odo/odo-file-index.json to determine deleted files
|
||||
Show bool // Show tells whether the devfile command output should be shown on stdout
|
||||
DevfileScanIndexForWatch bool // DevfileScanIndexForWatch is true if watch's push should regenerate the index file during SyncFiles, false otherwise. See 'pkg/sync/adapter.go' for details
|
||||
}
|
||||
@@ -2,8 +2,9 @@ package dev
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/redhat-developer/odo/pkg/api"
|
||||
"io"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/api"
|
||||
)
|
||||
|
||||
type StartOptions struct {
|
||||
@@ -31,6 +32,9 @@ type StartOptions struct {
|
||||
ForwardLocalhost bool
|
||||
// Variables to override in the Devfile
|
||||
Variables map[string]string
|
||||
|
||||
Out io.Writer
|
||||
ErrOut io.Writer
|
||||
}
|
||||
|
||||
type Client interface {
|
||||
@@ -39,8 +43,6 @@ type Client interface {
|
||||
// It logs messages and errors to out and errOut.
|
||||
Start(
|
||||
ctx context.Context,
|
||||
out io.Writer,
|
||||
errOut io.Writer,
|
||||
options StartOptions,
|
||||
) error
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
package component
|
||||
package kubedev
|
||||
|
||||
import (
|
||||
"context"
|
||||
216
pkg/dev/kubedev/innerloop.go
Normal file
216
pkg/dev/kubedev/innerloop.go
Normal file
@@ -0,0 +1,216 @@
|
||||
package kubedev
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
devfilev1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
|
||||
parsercommon "github.com/devfile/library/v2/pkg/devfile/parser/data/v2/common"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/component"
|
||||
"github.com/redhat-developer/odo/pkg/dev/common"
|
||||
"github.com/redhat-developer/odo/pkg/libdevfile"
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
odocontext "github.com/redhat-developer/odo/pkg/odo/context"
|
||||
"github.com/redhat-developer/odo/pkg/port"
|
||||
"github.com/redhat-developer/odo/pkg/sync"
|
||||
"github.com/redhat-developer/odo/pkg/util"
|
||||
"github.com/redhat-developer/odo/pkg/watch"
|
||||
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
func (o *DevClient) innerloop(ctx context.Context, parameters common.PushParameters, componentStatus *watch.ComponentStatus) error {
|
||||
var (
|
||||
appName = odocontext.GetApplication(ctx)
|
||||
componentName = odocontext.GetComponentName(ctx)
|
||||
devfilePath = odocontext.GetDevfilePath(ctx)
|
||||
path = filepath.Dir(devfilePath)
|
||||
)
|
||||
|
||||
// Now the Deployment has a Ready replica, we can get the Pod to work inside it
|
||||
pod, err := o.kubernetesClient.GetPodUsingComponentName(componentName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get pod for component %s: %w", componentName, err)
|
||||
}
|
||||
|
||||
// Find at least one pod with the source volume mounted, error out if none can be found
|
||||
containerName, syncFolder, err := common.GetFirstContainerWithSourceVolume(pod.Spec.Containers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while retrieving container from pod %s with a mounted project volume: %w", pod.GetName(), err)
|
||||
}
|
||||
|
||||
s := log.Spinner("Syncing files into the container")
|
||||
defer s.End(false)
|
||||
|
||||
// Get commands
|
||||
pushDevfileCommands, err := o.getPushDevfileCommands(parameters)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to validate devfile build and run commands: %w", err)
|
||||
}
|
||||
|
||||
podChanged := componentStatus.State == watch.StateWaitDeployment
|
||||
|
||||
// Get a sync adapter. Check if project files have changed and sync accordingly
|
||||
compInfo := sync.ComponentInfo{
|
||||
ComponentName: componentName,
|
||||
ContainerName: containerName,
|
||||
PodName: pod.GetName(),
|
||||
SyncFolder: syncFolder,
|
||||
}
|
||||
|
||||
cmdKind := devfilev1.RunCommandGroupKind
|
||||
cmdName := parameters.StartOptions.RunCommand
|
||||
if parameters.StartOptions.Debug {
|
||||
cmdKind = devfilev1.DebugCommandGroupKind
|
||||
cmdName = parameters.StartOptions.DebugCommand
|
||||
}
|
||||
|
||||
syncParams := sync.SyncParameters{
|
||||
Path: path,
|
||||
WatchFiles: parameters.WatchFiles,
|
||||
WatchDeletedFiles: parameters.WatchDeletedFiles,
|
||||
IgnoredFiles: parameters.StartOptions.IgnorePaths,
|
||||
DevfileScanIndexForWatch: parameters.DevfileScanIndexForWatch,
|
||||
|
||||
CompInfo: compInfo,
|
||||
ForcePush: !o.deploymentExists || podChanged,
|
||||
Files: common.GetSyncFilesFromAttributes(pushDevfileCommands[cmdKind]),
|
||||
}
|
||||
|
||||
execRequired, err := o.syncClient.SyncFiles(ctx, syncParams)
|
||||
if err != nil {
|
||||
componentStatus.State = watch.StateReady
|
||||
return fmt.Errorf("failed to sync to component with name %s: %w", componentName, err)
|
||||
}
|
||||
s.End(true)
|
||||
|
||||
// PostStart events from the devfile will only be executed when the component
|
||||
// didn't previously exist
|
||||
if !componentStatus.PostStartEventsDone && libdevfile.HasPostStartEvents(parameters.Devfile) {
|
||||
err = libdevfile.ExecPostStartEvents(ctx, parameters.Devfile, component.NewExecHandler(o.kubernetesClient, o.execClient, appName, componentName, pod.Name, "Executing post-start command in container", parameters.Show))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
componentStatus.PostStartEventsDone = true
|
||||
|
||||
cmd, err := libdevfile.ValidateAndGetCommand(parameters.Devfile, cmdName, cmdKind)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
commandType, err := parsercommon.GetCommandType(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var running bool
|
||||
var isComposite bool
|
||||
cmdHandler := runHandler{
|
||||
fs: o.filesystem,
|
||||
execClient: o.execClient,
|
||||
kubeClient: o.kubernetesClient,
|
||||
appName: appName,
|
||||
componentName: componentName,
|
||||
devfile: parameters.Devfile,
|
||||
path: path,
|
||||
podName: pod.GetName(),
|
||||
ctx: ctx,
|
||||
}
|
||||
|
||||
if commandType == devfilev1.ExecCommandType {
|
||||
running, err = cmdHandler.IsRemoteProcessForCommandRunning(ctx, cmd, pod.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if commandType == devfilev1.CompositeCommandType {
|
||||
// this handler will run each command in this composite command individually,
|
||||
// and will determine whether each command is running or not.
|
||||
isComposite = true
|
||||
} else {
|
||||
return fmt.Errorf("unsupported type %q for Devfile command %s, only exec and composite are handled",
|
||||
commandType, cmd.Id)
|
||||
}
|
||||
|
||||
cmdHandler.componentExists = running || isComposite
|
||||
|
||||
klog.V(4).Infof("running=%v, execRequired=%v",
|
||||
running, execRequired)
|
||||
|
||||
if isComposite || !running || execRequired {
|
||||
// Invoke the build command once (before calling libdevfile.ExecuteCommandByNameAndKind), as, if cmd is a composite command,
|
||||
// the handler we pass will be called for each command in that composite command.
|
||||
doExecuteBuildCommand := func() error {
|
||||
execHandler := component.NewExecHandler(o.kubernetesClient, o.execClient, appName, componentName, pod.Name,
|
||||
"Building your application in container", parameters.Show)
|
||||
return libdevfile.Build(ctx, parameters.Devfile, parameters.StartOptions.BuildCommand, execHandler)
|
||||
}
|
||||
if running {
|
||||
if cmd.Exec == nil || !util.SafeGetBool(cmd.Exec.HotReloadCapable) {
|
||||
if err = doExecuteBuildCommand(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err = doExecuteBuildCommand(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = libdevfile.ExecuteCommandByNameAndKind(ctx, parameters.Devfile, cmdName, cmdKind, &cmdHandler, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if podChanged || o.portsChanged {
|
||||
o.portForwardClient.StopPortForwarding(ctx, componentName)
|
||||
}
|
||||
|
||||
// Check that the application is actually listening on the ports declared in the Devfile, so we are sure that port-forwarding will work
|
||||
appReadySpinner := log.Spinner("Waiting for the application to be ready")
|
||||
err = o.checkAppPorts(ctx, pod.Name, o.portsToForward)
|
||||
appReadySpinner.End(err == nil)
|
||||
if err != nil {
|
||||
log.Warningf("Port forwarding might not work correctly: %v", err)
|
||||
log.Warning("Running `odo logs --follow` might help in identifying the problem.")
|
||||
fmt.Fprintln(log.GetStdout())
|
||||
}
|
||||
|
||||
err = o.portForwardClient.StartPortForwarding(ctx, parameters.Devfile, componentName, parameters.StartOptions.Debug, parameters.StartOptions.RandomPorts, log.GetStdout(), parameters.StartOptions.ErrOut, parameters.StartOptions.CustomForwardedPorts)
|
||||
if err != nil {
|
||||
return common.NewErrPortForward(err)
|
||||
}
|
||||
componentStatus.EndpointsForwarded = o.portForwardClient.GetForwardedPorts()
|
||||
|
||||
componentStatus.State = watch.StateReady
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *DevClient) getPushDevfileCommands(parameters common.PushParameters) (map[devfilev1.CommandGroupKind]devfilev1.Command, error) {
|
||||
pushDevfileCommands, err := libdevfile.ValidateAndGetPushCommands(parameters.Devfile, parameters.StartOptions.BuildCommand, parameters.StartOptions.RunCommand)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to validate devfile build and run commands: %w", err)
|
||||
}
|
||||
|
||||
if parameters.StartOptions.Debug {
|
||||
pushDevfileDebugCommands, e := libdevfile.ValidateAndGetCommand(parameters.Devfile, parameters.StartOptions.DebugCommand, devfilev1.DebugCommandGroupKind)
|
||||
if e != nil {
|
||||
return nil, fmt.Errorf("debug command is not valid: %w", e)
|
||||
}
|
||||
pushDevfileCommands[devfilev1.DebugCommandGroupKind] = pushDevfileDebugCommands
|
||||
}
|
||||
|
||||
return pushDevfileCommands, nil
|
||||
}
|
||||
|
||||
func (o *DevClient) checkAppPorts(ctx context.Context, podName string, portsToFwd map[string][]devfilev1.Endpoint) error {
|
||||
containerPortsMapping := make(map[string][]int)
|
||||
for c, ports := range portsToFwd {
|
||||
for _, p := range ports {
|
||||
containerPortsMapping[c] = append(containerPortsMapping[c], p.TargetPort)
|
||||
}
|
||||
}
|
||||
return port.CheckAppPortsListening(ctx, o.execClient, podName, containerPortsMapping, 1*time.Minute)
|
||||
}
|
||||
@@ -3,30 +3,26 @@ package kubedev
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
|
||||
devfilev1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/binding"
|
||||
_delete "github.com/redhat-developer/odo/pkg/component/delete"
|
||||
"github.com/redhat-developer/odo/pkg/configAutomount"
|
||||
"github.com/redhat-developer/odo/pkg/dev"
|
||||
"github.com/redhat-developer/odo/pkg/dev/common"
|
||||
"github.com/redhat-developer/odo/pkg/devfile"
|
||||
"github.com/redhat-developer/odo/pkg/devfile/location"
|
||||
"github.com/redhat-developer/odo/pkg/exec"
|
||||
"github.com/redhat-developer/odo/pkg/kclient"
|
||||
odocontext "github.com/redhat-developer/odo/pkg/odo/context"
|
||||
"github.com/redhat-developer/odo/pkg/portForward"
|
||||
"github.com/redhat-developer/odo/pkg/preference"
|
||||
"github.com/redhat-developer/odo/pkg/sync"
|
||||
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||
"github.com/redhat-developer/odo/pkg/watch"
|
||||
|
||||
"k8s.io/klog"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/devfile/adapters"
|
||||
"github.com/redhat-developer/odo/pkg/devfile/adapters/kubernetes/component"
|
||||
"github.com/redhat-developer/odo/pkg/devfile/location"
|
||||
odocontext "github.com/redhat-developer/odo/pkg/odo/context"
|
||||
"github.com/redhat-developer/odo/pkg/watch"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -47,6 +43,13 @@ type DevClient struct {
|
||||
execClient exec.Client
|
||||
deleteClient _delete.Client
|
||||
configAutomountClient configAutomount.Client
|
||||
|
||||
// deploymentExists is true when the deployment is already created when calling createComponents
|
||||
deploymentExists bool
|
||||
// portsChanged is true of ports have changed since the last call to createComponents
|
||||
portsChanged bool
|
||||
// portsToForward lists the port to forward during inner loop (TODO move port forward to createComponents)
|
||||
portsToForward map[string][]devfilev1.Endpoint
|
||||
}
|
||||
|
||||
var _ dev.Client = (*DevClient)(nil)
|
||||
@@ -79,116 +82,52 @@ func NewDevClient(
|
||||
|
||||
func (o *DevClient) Start(
|
||||
ctx context.Context,
|
||||
out io.Writer,
|
||||
errOut io.Writer,
|
||||
options dev.StartOptions,
|
||||
) error {
|
||||
klog.V(4).Infoln("Creating new adapter")
|
||||
|
||||
var (
|
||||
devfileObj = odocontext.GetDevfileObj(ctx)
|
||||
devfilePath = odocontext.GetDevfilePath(ctx)
|
||||
path = filepath.Dir(devfilePath)
|
||||
componentName = odocontext.GetComponentName(ctx)
|
||||
devfileObj = odocontext.GetDevfileObj(ctx)
|
||||
)
|
||||
|
||||
adapter := component.NewKubernetesAdapter(
|
||||
o.kubernetesClient,
|
||||
o.prefClient,
|
||||
o.portForwardClient,
|
||||
o.bindingClient,
|
||||
o.syncClient,
|
||||
o.execClient,
|
||||
o.configAutomountClient,
|
||||
component.AdapterContext{
|
||||
ComponentName: componentName,
|
||||
Context: path,
|
||||
AppName: odocontext.GetApplication(ctx),
|
||||
Devfile: *devfileObj,
|
||||
FS: o.filesystem,
|
||||
})
|
||||
|
||||
pushParameters := adapters.PushParameters{
|
||||
Path: path,
|
||||
IgnoredFiles: options.IgnorePaths,
|
||||
Debug: options.Debug,
|
||||
DevfileBuildCmd: options.BuildCommand,
|
||||
DevfileRunCmd: options.RunCommand,
|
||||
RandomPorts: options.RandomPorts,
|
||||
CustomForwardedPorts: options.CustomForwardedPorts,
|
||||
ErrOut: errOut,
|
||||
pushParameters := common.PushParameters{
|
||||
StartOptions: options,
|
||||
Devfile: *devfileObj,
|
||||
}
|
||||
|
||||
klog.V(4).Infoln("Creating inner-loop resources for the component")
|
||||
componentStatus := watch.ComponentStatus{
|
||||
ImageComponentsAutoApplied: make(map[string]v1alpha2.ImageComponent),
|
||||
ImageComponentsAutoApplied: make(map[string]devfilev1.ImageComponent),
|
||||
}
|
||||
err := adapter.Push(ctx, pushParameters, &componentStatus)
|
||||
err := o.reconcile(ctx, pushParameters, &componentStatus)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
klog.V(4).Infoln("Successfully created inner-loop resources")
|
||||
|
||||
watchParameters := watch.WatchParameters{
|
||||
DevfilePath: devfilePath,
|
||||
Path: path,
|
||||
ComponentName: componentName,
|
||||
ApplicationName: odocontext.GetApplication(ctx),
|
||||
DevfileWatchHandler: o.regenerateAdapterAndPush,
|
||||
FileIgnores: options.IgnorePaths,
|
||||
InitialDevfileObj: *devfileObj,
|
||||
Debug: options.Debug,
|
||||
DevfileBuildCmd: options.BuildCommand,
|
||||
DevfileRunCmd: options.RunCommand,
|
||||
Variables: options.Variables,
|
||||
RandomPorts: options.RandomPorts,
|
||||
CustomForwardedPorts: options.CustomForwardedPorts,
|
||||
WatchFiles: options.WatchFiles,
|
||||
WatchCluster: true,
|
||||
ErrOut: errOut,
|
||||
PromptMessage: promptMessage,
|
||||
StartOptions: options,
|
||||
DevfileWatchHandler: o.regenerateAdapterAndPush,
|
||||
WatchCluster: true,
|
||||
PromptMessage: promptMessage,
|
||||
}
|
||||
|
||||
return o.watchClient.WatchAndPush(out, watchParameters, ctx, componentStatus)
|
||||
return o.watchClient.WatchAndPush(ctx, watchParameters, componentStatus)
|
||||
}
|
||||
|
||||
// RegenerateAdapterAndPush regenerates the adapter and pushes the files to remote pod
|
||||
func (o *DevClient) regenerateAdapterAndPush(ctx context.Context, pushParams adapters.PushParameters, watchParams watch.WatchParameters, componentStatus *watch.ComponentStatus) error {
|
||||
var adapter component.ComponentAdapter
|
||||
// RegenerateAdapterAndPush get the new devfile and pushes the files to remote pod
|
||||
func (o *DevClient) regenerateAdapterAndPush(ctx context.Context, pushParams common.PushParameters, componentStatus *watch.ComponentStatus) error {
|
||||
|
||||
adapter, err := o.regenerateComponentAdapterFromWatchParams(watchParams)
|
||||
devObj, err := devfile.ParseAndValidateFromFileWithVariables(location.DevfileLocation(""), pushParams.StartOptions.Variables)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to generate component from watch parameters: %w", err)
|
||||
}
|
||||
|
||||
err = adapter.Push(ctx, pushParams, componentStatus)
|
||||
pushParams.Devfile = devObj
|
||||
|
||||
err = o.reconcile(ctx, pushParams, componentStatus)
|
||||
if err != nil {
|
||||
return fmt.Errorf("watch command was unable to push component: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *DevClient) regenerateComponentAdapterFromWatchParams(parameters watch.WatchParameters) (component.ComponentAdapter, error) {
|
||||
devObj, err := devfile.ParseAndValidateFromFileWithVariables(location.DevfileLocation(""), parameters.Variables)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return component.NewKubernetesAdapter(
|
||||
o.kubernetesClient,
|
||||
o.prefClient,
|
||||
o.portForwardClient,
|
||||
o.bindingClient,
|
||||
o.syncClient,
|
||||
o.execClient,
|
||||
o.configAutomountClient,
|
||||
component.AdapterContext{
|
||||
ComponentName: parameters.ComponentName,
|
||||
Context: parameters.Path,
|
||||
AppName: parameters.ApplicationName,
|
||||
Devfile: devObj,
|
||||
FS: o.filesystem,
|
||||
},
|
||||
), nil
|
||||
}
|
||||
|
||||
@@ -1,34 +1,35 @@
|
||||
package component
|
||||
package kubedev
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/devfile/library/v2/pkg/devfile/generator"
|
||||
"github.com/devfile/library/v2/pkg/devfile/parser/data"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/configAutomount"
|
||||
"github.com/redhat-developer/odo/pkg/libdevfile"
|
||||
"github.com/redhat-developer/odo/pkg/preference"
|
||||
"github.com/redhat-developer/odo/pkg/util"
|
||||
|
||||
devfilev1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
|
||||
"github.com/devfile/library/v2/pkg/devfile/generator"
|
||||
devfileParser "github.com/devfile/library/v2/pkg/devfile/parser"
|
||||
"github.com/devfile/library/v2/pkg/testingutil"
|
||||
"github.com/devfile/library/v2/pkg/devfile/parser/data"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/configAutomount"
|
||||
"github.com/redhat-developer/odo/pkg/dev/common"
|
||||
"github.com/redhat-developer/odo/pkg/kclient"
|
||||
odolabels "github.com/redhat-developer/odo/pkg/labels"
|
||||
"github.com/redhat-developer/odo/pkg/libdevfile"
|
||||
odocontext "github.com/redhat-developer/odo/pkg/odo/context"
|
||||
"github.com/redhat-developer/odo/pkg/preference"
|
||||
odoTestingUtil "github.com/redhat-developer/odo/pkg/testingutil"
|
||||
"github.com/redhat-developer/odo/pkg/util"
|
||||
|
||||
v1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
ktesting "k8s.io/client-go/testing"
|
||||
)
|
||||
|
||||
@@ -84,7 +85,7 @@ func TestCreateOrUpdateComponent(t *testing.T) {
|
||||
var comp devfilev1.Component
|
||||
if tt.componentType != "" {
|
||||
odolabels.SetProjectType(deployment.Annotations, string(tt.componentType))
|
||||
comp = testingutil.GetFakeContainerComponent("component")
|
||||
comp = odoTestingUtil.GetFakeContainerComponent("component")
|
||||
}
|
||||
devObj := devfileParser.DevfileObj{
|
||||
Data: func() data.DevfileData {
|
||||
@@ -106,12 +107,6 @@ func TestCreateOrUpdateComponent(t *testing.T) {
|
||||
}(),
|
||||
}
|
||||
|
||||
adapterCtx := AdapterContext{
|
||||
ComponentName: testComponentName,
|
||||
AppName: testAppName,
|
||||
Devfile: devObj,
|
||||
}
|
||||
|
||||
fkclient, fkclientset := kclient.FakeNew()
|
||||
|
||||
fkclientset.Kubernetes.PrependReactor("patch", "deployments", func(action ktesting.Action) (bool, runtime.Object, error) {
|
||||
@@ -134,8 +129,14 @@ func TestCreateOrUpdateComponent(t *testing.T) {
|
||||
fakePrefClient.EXPECT().GetEphemeralSourceVolume().AnyTimes()
|
||||
fakeConfigAutomount := configAutomount.NewMockClient(ctrl)
|
||||
fakeConfigAutomount.EXPECT().GetAutomountingVolumes().AnyTimes()
|
||||
componentAdapter := NewKubernetesAdapter(fkclient, fakePrefClient, nil, nil, nil, nil, fakeConfigAutomount, adapterCtx)
|
||||
_, _, err := componentAdapter.createOrUpdateComponent(tt.running, libdevfile.DevfileCommands{}, nil)
|
||||
client := NewDevClient(fkclient, fakePrefClient, nil, nil, nil, nil, nil, nil, nil, fakeConfigAutomount)
|
||||
ctx := context.Background()
|
||||
ctx = odocontext.WithApplication(ctx, "app")
|
||||
ctx = odocontext.WithComponentName(ctx, "my-component")
|
||||
ctx = odocontext.WithDevfilePath(ctx, "/path/to/devfile")
|
||||
_, _, err := client.createOrUpdateComponent(ctx, common.PushParameters{
|
||||
Devfile: devObj,
|
||||
}, tt.running, libdevfile.DevfileCommands{}, nil)
|
||||
|
||||
// Checks for unexpected error cases
|
||||
if !tt.wantErr == (err != nil) {
|
||||
@@ -240,14 +241,14 @@ func TestAdapter_generateDeploymentObjectMeta(t *testing.T) {
|
||||
fakeClient, _ := kclient.FakeNew()
|
||||
fakeClient.Namespace = "project-0"
|
||||
|
||||
a := Adapter{
|
||||
kubeClient: fakeClient,
|
||||
AdapterContext: AdapterContext{
|
||||
ComponentName: tt.fields.componentName,
|
||||
AppName: tt.fields.appName,
|
||||
},
|
||||
a := DevClient{
|
||||
kubernetesClient: fakeClient,
|
||||
}
|
||||
got, err := a.generateDeploymentObjectMeta(tt.fields.deployment, tt.args.labels, tt.args.annotations)
|
||||
ctx := context.Background()
|
||||
ctx = odocontext.WithApplication(ctx, "app")
|
||||
ctx = odocontext.WithComponentName(ctx, "nodejs")
|
||||
ctx = odocontext.WithDevfilePath(ctx, "/path/to/devfile")
|
||||
got, err := a.generateDeploymentObjectMeta(ctx, tt.fields.deployment, tt.args.labels, tt.args.annotations)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("generateDeploymentObjectMeta() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
@@ -436,8 +437,8 @@ func TestAdapter_deleteRemoteResources(t *testing.T) {
|
||||
if tt.fields.kubeClientCustomizer != nil {
|
||||
tt.fields.kubeClientCustomizer(kubeClient)
|
||||
}
|
||||
a := Adapter{
|
||||
kubeClient: kubeClient,
|
||||
a := DevClient{
|
||||
kubernetesClient: kubeClient,
|
||||
}
|
||||
if err := a.deleteRemoteResources(tt.args.objectsToRemove); (err != nil) != tt.wantErr {
|
||||
t.Errorf("deleteRemoteResources() error = %v, wantErr %v", err, tt.wantErr)
|
||||
26
pkg/dev/kubedev/reconcile.go
Normal file
26
pkg/dev/kubedev/reconcile.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package kubedev
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/dev/common"
|
||||
"github.com/redhat-developer/odo/pkg/watch"
|
||||
)
|
||||
|
||||
// reconcile updates the component if a matching component exists or creates one if it doesn't exist
|
||||
// Once the component has started, it will sync the source code to it.
|
||||
// The componentStatus will be modified to reflect the status of the component when the function returns
|
||||
func (o *DevClient) reconcile(ctx context.Context, parameters common.PushParameters, componentStatus *watch.ComponentStatus) (err error) {
|
||||
|
||||
// podOK indicates if the pod is ready to use for the inner loop
|
||||
var podOK bool
|
||||
podOK, err = o.createComponents(ctx, parameters, componentStatus)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !podOK {
|
||||
return nil
|
||||
}
|
||||
|
||||
return o.innerloop(ctx, parameters, componentStatus)
|
||||
}
|
||||
@@ -50,15 +50,15 @@ func (mr *MockClientMockRecorder) CleanupResources(ctx, out interface{}) *gomock
|
||||
}
|
||||
|
||||
// Start mocks base method.
|
||||
func (m *MockClient) Start(ctx context.Context, out, errOut io.Writer, options StartOptions) error {
|
||||
func (m *MockClient) Start(ctx context.Context, options StartOptions) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Start", ctx, out, errOut, options)
|
||||
ret := m.ctrl.Call(m, "Start", ctx, options)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Start indicates an expected call of Start.
|
||||
func (mr *MockClientMockRecorder) Start(ctx, out, errOut, options interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) Start(ctx, options interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockClient)(nil).Start), ctx, out, errOut, options)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockClient)(nil).Start), ctx, options)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/api"
|
||||
"github.com/redhat-developer/odo/pkg/component"
|
||||
"github.com/redhat-developer/odo/pkg/devfile/adapters/kubernetes/utils"
|
||||
"github.com/redhat-developer/odo/pkg/dev/kubedev/utils"
|
||||
"github.com/redhat-developer/odo/pkg/labels"
|
||||
"github.com/redhat-developer/odo/pkg/libdevfile"
|
||||
"github.com/redhat-developer/odo/pkg/odo/commonflags"
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
@@ -16,7 +15,6 @@ import (
|
||||
"github.com/redhat-developer/odo/pkg/dev"
|
||||
"github.com/redhat-developer/odo/pkg/dev/common"
|
||||
"github.com/redhat-developer/odo/pkg/devfile"
|
||||
"github.com/redhat-developer/odo/pkg/devfile/adapters"
|
||||
"github.com/redhat-developer/odo/pkg/devfile/location"
|
||||
"github.com/redhat-developer/odo/pkg/exec"
|
||||
"github.com/redhat-developer/odo/pkg/libdevfile"
|
||||
@@ -77,53 +75,32 @@ func NewDevClient(
|
||||
|
||||
func (o *DevClient) Start(
|
||||
ctx context.Context,
|
||||
out io.Writer,
|
||||
errOut io.Writer,
|
||||
options dev.StartOptions,
|
||||
) error {
|
||||
var (
|
||||
appName = odocontext.GetApplication(ctx)
|
||||
componentName = odocontext.GetComponentName(ctx)
|
||||
devfileObj = odocontext.GetDevfileObj(ctx)
|
||||
devfilePath = odocontext.GetDevfilePath(ctx)
|
||||
path = filepath.Dir(devfilePath)
|
||||
devfilePath = odocontext.GetDevfilePath(ctx)
|
||||
path = filepath.Dir(devfilePath)
|
||||
|
||||
componentStatus = watch.ComponentStatus{
|
||||
ImageComponentsAutoApplied: make(map[string]devfilev1.ImageComponent),
|
||||
}
|
||||
)
|
||||
|
||||
err := o.reconcile(ctx, out, errOut, options, &componentStatus)
|
||||
err := o.reconcile(ctx, options, &componentStatus)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
watch.PrintInfoMessage(out, path, options.WatchFiles, promptMessage)
|
||||
watch.PrintInfoMessage(options.Out, path, options.WatchFiles, promptMessage)
|
||||
|
||||
watchParameters := watch.WatchParameters{
|
||||
DevfilePath: devfilePath,
|
||||
Path: path,
|
||||
ComponentName: componentName,
|
||||
ApplicationName: appName,
|
||||
InitialDevfileObj: *devfileObj,
|
||||
DevfileWatchHandler: o.watchHandler,
|
||||
FileIgnores: options.IgnorePaths,
|
||||
Debug: options.Debug,
|
||||
DevfileBuildCmd: options.BuildCommand,
|
||||
DevfileRunCmd: options.RunCommand,
|
||||
Variables: options.Variables,
|
||||
RandomPorts: options.RandomPorts,
|
||||
IgnoreLocalhost: options.IgnoreLocalhost,
|
||||
ForwardLocalhost: options.ForwardLocalhost,
|
||||
CustomForwardedPorts: options.CustomForwardedPorts,
|
||||
WatchFiles: options.WatchFiles,
|
||||
WatchCluster: false,
|
||||
Out: out,
|
||||
ErrOut: errOut,
|
||||
PromptMessage: promptMessage,
|
||||
StartOptions: options,
|
||||
DevfileWatchHandler: o.watchHandler,
|
||||
WatchCluster: false,
|
||||
PromptMessage: promptMessage,
|
||||
}
|
||||
|
||||
return o.watchClient.WatchAndPush(out, watchParameters, ctx, componentStatus)
|
||||
return o.watchClient.WatchAndPush(ctx, watchParameters, componentStatus)
|
||||
}
|
||||
|
||||
// syncFiles syncs the local source files in path into the pod's source volume
|
||||
@@ -165,7 +142,7 @@ func (o *DevClient) syncFiles(ctx context.Context, options dev.StartOptions, pod
|
||||
|
||||
CompInfo: compInfo,
|
||||
ForcePush: true,
|
||||
Files: adapters.GetSyncFilesFromAttributes(devfileCmd),
|
||||
Files: common.GetSyncFilesFromAttributes(devfileCmd),
|
||||
}
|
||||
execRequired, err := o.syncClient.SyncFiles(ctx, syncParams)
|
||||
if err != nil {
|
||||
@@ -193,28 +170,15 @@ func (o *DevClient) checkVolumesFree(pod *corev1.Pod) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *DevClient) watchHandler(ctx context.Context, pushParams adapters.PushParameters, watchParams watch.WatchParameters, componentStatus *watch.ComponentStatus) error {
|
||||
printWarningsOnDevfileChanges(ctx, watchParams)
|
||||
|
||||
startOptions := dev.StartOptions{
|
||||
IgnorePaths: watchParams.FileIgnores,
|
||||
Debug: watchParams.Debug,
|
||||
BuildCommand: watchParams.DevfileBuildCmd,
|
||||
RunCommand: watchParams.DevfileRunCmd,
|
||||
RandomPorts: watchParams.RandomPorts,
|
||||
IgnoreLocalhost: watchParams.IgnoreLocalhost,
|
||||
ForwardLocalhost: watchParams.ForwardLocalhost,
|
||||
CustomForwardedPorts: watchParams.CustomForwardedPorts,
|
||||
WatchFiles: watchParams.WatchFiles,
|
||||
Variables: watchParams.Variables,
|
||||
}
|
||||
return o.reconcile(ctx, watchParams.Out, watchParams.ErrOut, startOptions, componentStatus)
|
||||
func (o *DevClient) watchHandler(ctx context.Context, pushParams common.PushParameters, componentStatus *watch.ComponentStatus) error {
|
||||
printWarningsOnDevfileChanges(ctx, pushParams.StartOptions)
|
||||
return o.reconcile(ctx, pushParams.StartOptions, componentStatus)
|
||||
}
|
||||
|
||||
func printWarningsOnDevfileChanges(ctx context.Context, parameters watch.WatchParameters) {
|
||||
func printWarningsOnDevfileChanges(ctx context.Context, options dev.StartOptions) {
|
||||
var warning string
|
||||
currentDevfile := odocontext.GetDevfileObj(ctx)
|
||||
newDevfile, err := devfile.ParseAndValidateFromFileWithVariables(location.DevfileLocation(""), parameters.Variables)
|
||||
newDevfile, err := devfile.ParseAndValidateFromFileWithVariables(location.DevfileLocation(""), options.Variables)
|
||||
if err != nil {
|
||||
warning = fmt.Sprintf("error while reading the Devfile. Please restart 'odo dev' if you made any changes to the Devfile. Error message is: %v", err)
|
||||
} else {
|
||||
@@ -239,6 +203,6 @@ func printWarningsOnDevfileChanges(ctx context.Context, parameters watch.WatchPa
|
||||
}
|
||||
}
|
||||
if warning != "" {
|
||||
log.Fwarning(parameters.Out, warning+"\n")
|
||||
log.Fwarning(options.Out, warning+"\n")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -17,7 +16,7 @@ import (
|
||||
"github.com/redhat-developer/odo/pkg/component"
|
||||
envcontext "github.com/redhat-developer/odo/pkg/config/context"
|
||||
"github.com/redhat-developer/odo/pkg/dev"
|
||||
"github.com/redhat-developer/odo/pkg/devfile/adapters"
|
||||
"github.com/redhat-developer/odo/pkg/dev/common"
|
||||
"github.com/redhat-developer/odo/pkg/devfile/image"
|
||||
"github.com/redhat-developer/odo/pkg/libdevfile"
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
@@ -32,8 +31,6 @@ import (
|
||||
|
||||
func (o *DevClient) reconcile(
|
||||
ctx context.Context,
|
||||
out io.Writer,
|
||||
errOut io.Writer,
|
||||
options dev.StartOptions,
|
||||
componentStatus *watch.ComponentStatus,
|
||||
) error {
|
||||
@@ -130,7 +127,7 @@ func (o *DevClient) reconcile(
|
||||
if err != nil {
|
||||
log.Warningf("Port forwarding might not work correctly: %v", err)
|
||||
log.Warning("Running `odo logs --follow --platform podman` might help in identifying the problem.")
|
||||
fmt.Fprintln(out)
|
||||
fmt.Fprintln(options.Out)
|
||||
}
|
||||
|
||||
// By default, Podman will not forward to container applications listening on the loopback interface.
|
||||
@@ -143,15 +140,15 @@ func (o *DevClient) reconcile(
|
||||
|
||||
if options.ForwardLocalhost {
|
||||
// Port-forwarding is enabled by executing dedicated socat commands
|
||||
err = o.portForwardClient.StartPortForwarding(ctx, *devfileObj, componentName, options.Debug, options.RandomPorts, out, errOut, fwPorts)
|
||||
err = o.portForwardClient.StartPortForwarding(ctx, *devfileObj, componentName, options.Debug, options.RandomPorts, options.Out, options.ErrOut, fwPorts)
|
||||
if err != nil {
|
||||
return adapters.NewErrPortForward(err)
|
||||
return common.NewErrPortForward(err)
|
||||
}
|
||||
} // else port-forwarding is done via the main container ports in the pod spec
|
||||
|
||||
for _, fwPort := range fwPorts {
|
||||
s := fmt.Sprintf("Forwarding from %s:%d -> %d", fwPort.LocalAddress, fwPort.LocalPort, fwPort.ContainerPort)
|
||||
fmt.Fprintf(out, " - %s", log.SboldColor(color.FgGreen, s))
|
||||
fmt.Fprintf(options.Out, " - %s", log.SboldColor(color.FgGreen, s))
|
||||
}
|
||||
err = o.stateClient.SetForwardedPorts(ctx, fwPorts)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package component
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/devfile/adapters"
|
||||
"github.com/redhat-developer/odo/pkg/watch"
|
||||
)
|
||||
|
||||
// ComponentAdapter defines the functions that platform-specific adapters must implement
|
||||
type ComponentAdapter interface {
|
||||
Push(ctx context.Context, parameters adapters.PushParameters, componentStatus *watch.ComponentStatus) error
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
package component
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
devfilev1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
|
||||
"github.com/devfile/library/v2/pkg/devfile/parser"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/klog"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/component"
|
||||
"github.com/redhat-developer/odo/pkg/devfile/adapters"
|
||||
"github.com/redhat-developer/odo/pkg/devfile/image"
|
||||
"github.com/redhat-developer/odo/pkg/kclient"
|
||||
odolabels "github.com/redhat-developer/odo/pkg/labels"
|
||||
"github.com/redhat-developer/odo/pkg/libdevfile"
|
||||
"github.com/redhat-developer/odo/pkg/service"
|
||||
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||
"github.com/redhat-developer/odo/pkg/watch"
|
||||
)
|
||||
|
||||
// getComponentDeployment returns the deployment associated with the component, if deployed
|
||||
// and indicate if the deployment has been found
|
||||
func (a *Adapter) getComponentDeployment() (*appsv1.Deployment, bool, error) {
|
||||
// Get the Dev deployment:
|
||||
// Since `odo deploy` can theoretically deploy a deployment as well with the same instance name
|
||||
// we make sure that we are retrieving the deployment with the Dev mode, NOT Deploy.
|
||||
selectorLabels := odolabels.GetSelector(a.ComponentName, a.AppName, odolabels.ComponentDevMode, true)
|
||||
deployment, err := a.kubeClient.GetOneDeploymentFromSelector(selectorLabels)
|
||||
|
||||
if err != nil {
|
||||
if _, ok := err.(*kclient.DeploymentNotFoundError); !ok {
|
||||
return nil, false, fmt.Errorf("unable to determine if component %s exists: %w", a.ComponentName, err)
|
||||
}
|
||||
}
|
||||
componentExists := deployment != nil
|
||||
return deployment, componentExists, nil
|
||||
}
|
||||
|
||||
func (a *Adapter) buildPushAutoImageComponents(ctx context.Context, fs filesystem.Filesystem, devfileObj parser.DevfileObj, compStatus *watch.ComponentStatus) error {
|
||||
components, err := libdevfile.GetImageComponentsToPushAutomatically(devfileObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, c := range components {
|
||||
if c.Image == nil {
|
||||
return fmt.Errorf("component %q should be an Image Component", c.Name)
|
||||
}
|
||||
alreadyApplied, ok := compStatus.ImageComponentsAutoApplied[c.Name]
|
||||
if ok && reflect.DeepEqual(*c.Image, alreadyApplied) {
|
||||
klog.V(1).Infof("Skipping image component %q; already applied and not changed", c.Name)
|
||||
continue
|
||||
}
|
||||
err = image.BuildPushSpecificImage(ctx, fs, c, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
compStatus.ImageComponentsAutoApplied[c.Name] = *c.Image
|
||||
}
|
||||
|
||||
// Remove keys that might no longer be valid
|
||||
devfileHasCompFn := func(n string) bool {
|
||||
for _, c := range components {
|
||||
if c.Name == n {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
for n := range compStatus.ImageComponentsAutoApplied {
|
||||
if !devfileHasCompFn(n) {
|
||||
delete(compStatus.ImageComponentsAutoApplied, n)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// pushDevfileKubernetesComponents gets the Kubernetes components from the Devfile and push them to the cluster
|
||||
// adding the specified labels and ownerreference to them
|
||||
func (a *Adapter) pushDevfileKubernetesComponents(
|
||||
labels map[string]string,
|
||||
mode string,
|
||||
reference metav1.OwnerReference,
|
||||
) ([]devfilev1.Component, error) {
|
||||
// fetch the "kubernetes inlined components" to create them on cluster
|
||||
// from odo standpoint, these components contain yaml manifest of ServiceBinding
|
||||
k8sComponents, err := libdevfile.GetK8sAndOcComponentsToPush(a.Devfile, false)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error while trying to fetch service(s) from devfile: %w", err)
|
||||
}
|
||||
|
||||
// validate if the GVRs represented by Kubernetes inlined components are supported by the underlying cluster
|
||||
err = component.ValidateResourcesExist(a.kubeClient, a.Devfile, k8sComponents, a.Context)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set the annotations for the component type
|
||||
annotations := make(map[string]string)
|
||||
odolabels.SetProjectType(annotations, component.GetComponentTypeFromDevfileMetadata(a.AdapterContext.Devfile.Data.GetMetadata()))
|
||||
|
||||
// create the Kubernetes objects from the manifest and delete the ones not in the devfile
|
||||
err = service.PushKubernetesResources(a.kubeClient, a.Devfile, k8sComponents, labels, annotations, a.Context, mode, reference)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create Kubernetes resources associated with the component: %w", err)
|
||||
}
|
||||
return k8sComponents, nil
|
||||
}
|
||||
|
||||
func (a *Adapter) getPushDevfileCommands(parameters adapters.PushParameters) (map[devfilev1.CommandGroupKind]devfilev1.Command, error) {
|
||||
pushDevfileCommands, err := libdevfile.ValidateAndGetPushCommands(a.Devfile, parameters.DevfileBuildCmd, parameters.DevfileRunCmd)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to validate devfile build and run commands: %w", err)
|
||||
}
|
||||
|
||||
if parameters.Debug {
|
||||
pushDevfileDebugCommands, e := libdevfile.ValidateAndGetCommand(a.Devfile, parameters.DevfileDebugCmd, devfilev1.DebugCommandGroupKind)
|
||||
if e != nil {
|
||||
return nil, fmt.Errorf("debug command is not valid: %w", e)
|
||||
}
|
||||
pushDevfileCommands[devfilev1.DebugCommandGroupKind] = pushDevfileDebugCommands
|
||||
}
|
||||
|
||||
return pushDevfileCommands, nil
|
||||
}
|
||||
|
||||
func (a *Adapter) updatePVCsOwnerReferences(ownerReference metav1.OwnerReference) error {
|
||||
// list the latest state of the PVCs
|
||||
pvcs, err := a.kubeClient.ListPVCs(fmt.Sprintf("%v=%v", "component", a.ComponentName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// update the owner reference of the PVCs with the deployment
|
||||
for i := range pvcs {
|
||||
if pvcs[i].OwnerReferences != nil || pvcs[i].DeletionTimestamp != nil {
|
||||
continue
|
||||
}
|
||||
err = a.kubeClient.TryWithBlockOwnerDeletion(ownerReference, func(ownerRef metav1.OwnerReference) error {
|
||||
return a.kubeClient.UpdateStorageOwnerReference(&pvcs[i], ownerRef)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package adapters
|
||||
|
||||
import (
|
||||
"github.com/redhat-developer/odo/pkg/api"
|
||||
"io"
|
||||
)
|
||||
|
||||
// PushParameters is a struct containing the parameters to be used when pushing to a devfile component
|
||||
type PushParameters struct {
|
||||
Path string // Path refers to the parent folder containing the source code to push up to a component
|
||||
WatchFiles []string // Optional: WatchFiles is the list of changed files detected by odo watch. If empty or nil, odo will check .odo/odo-file-index.json to determine changed files
|
||||
WatchDeletedFiles []string // Optional: WatchDeletedFiles is the list of deleted files detected by odo watch. If empty or nil, odo will check .odo/odo-file-index.json to determine deleted files
|
||||
IgnoredFiles []string // IgnoredFiles is the list of files to not push up to a component
|
||||
Show bool // Show tells whether the devfile command output should be shown on stdout
|
||||
DevfileBuildCmd string // DevfileBuildCmd takes the build command through the command line and overwrites devfile build command
|
||||
DevfileRunCmd string // DevfileRunCmd takes the run command through the command line and overwrites devfile run command
|
||||
DevfileDebugCmd string // DevfileDebugCmd takes the debug command through the command line and overwrites the devfile debug command
|
||||
DevfileScanIndexForWatch bool // DevfileScanIndexForWatch is true if watch's push should regenerate the index file during SyncFiles, false otherwise. See 'pkg/sync/adapter.go' for details
|
||||
Debug bool // Runs the component in debug mode
|
||||
RandomPorts bool // True to forward containers ports on local random ports
|
||||
CustomForwardedPorts []api.ForwardedPort // Optional: CustomForwardedPorts configuration to be used to customize the port forwarding; if nil, we automatically select ports
|
||||
ErrOut io.Writer // Writer to output forwarded port information
|
||||
}
|
||||
@@ -238,8 +238,6 @@ func (o *DevOptions) Run(ctx context.Context) (err error) {
|
||||
|
||||
return o.clientset.DevClient.Start(
|
||||
o.ctx,
|
||||
o.out,
|
||||
o.errOut,
|
||||
dev.StartOptions{
|
||||
IgnorePaths: o.ignorePaths,
|
||||
Debug: o.debugFlag,
|
||||
@@ -251,6 +249,8 @@ func (o *DevOptions) Run(ctx context.Context) (err error) {
|
||||
ForwardLocalhost: o.forwardLocalhostFlag,
|
||||
Variables: variables,
|
||||
CustomForwardedPorts: o.forwardedPorts,
|
||||
Out: o.out,
|
||||
ErrOut: o.errOut,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package watch
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
)
|
||||
|
||||
type Client interface {
|
||||
@@ -11,5 +10,5 @@ type Client interface {
|
||||
// componentStatus is a variable to store the status of the component, and that will be exchanged between
|
||||
// parts of code (unfortunately, tthere is no place to store the status of the component in some Kubernetes resource
|
||||
// as it is generally done for a Kubernetes resource)
|
||||
WatchAndPush(out io.Writer, parameters WatchParameters, ctx context.Context, componentStatus ComponentStatus) error
|
||||
WatchAndPush(ctx context.Context, parameters WatchParameters, componentStatus ComponentStatus) error
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ package watch
|
||||
|
||||
import (
|
||||
context "context"
|
||||
io "io"
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
@@ -36,15 +35,15 @@ func (m *MockClient) EXPECT() *MockClientMockRecorder {
|
||||
}
|
||||
|
||||
// WatchAndPush mocks base method.
|
||||
func (m *MockClient) WatchAndPush(out io.Writer, parameters WatchParameters, ctx context.Context, componentStatus ComponentStatus) error {
|
||||
func (m *MockClient) WatchAndPush(ctx context.Context, parameters WatchParameters, componentStatus ComponentStatus) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "WatchAndPush", out, parameters, ctx, componentStatus)
|
||||
ret := m.ctrl.Call(m, "WatchAndPush", ctx, parameters, componentStatus)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// WatchAndPush indicates an expected call of WatchAndPush.
|
||||
func (mr *MockClientMockRecorder) WatchAndPush(out, parameters, ctx, componentStatus interface{}) *gomock.Call {
|
||||
func (mr *MockClientMockRecorder) WatchAndPush(ctx, parameters, componentStatus interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WatchAndPush", reflect.TypeOf((*MockClient)(nil).WatchAndPush), out, parameters, ctx, componentStatus)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WatchAndPush", reflect.TypeOf((*MockClient)(nil).WatchAndPush), ctx, parameters, componentStatus)
|
||||
}
|
||||
|
||||
@@ -4,20 +4,20 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/redhat-developer/odo/pkg/api"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/devfile/library/v2/pkg/devfile/parser"
|
||||
"github.com/redhat-developer/odo/pkg/dev"
|
||||
"github.com/redhat-developer/odo/pkg/dev/common"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/devfile/adapters"
|
||||
"github.com/redhat-developer/odo/pkg/kclient"
|
||||
"github.com/redhat-developer/odo/pkg/labels"
|
||||
"github.com/redhat-developer/odo/pkg/libdevfile"
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
odocontext "github.com/redhat-developer/odo/pkg/odo/context"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
gitignore "github.com/sabhiram/go-gitignore"
|
||||
@@ -59,54 +59,19 @@ func NewWatchClient(kubeClient kclient.ClientInterface) *WatchClient {
|
||||
|
||||
// WatchParameters is designed to hold the controllables and attributes that the watch function works on
|
||||
type WatchParameters struct {
|
||||
// Name of component that is to be watched
|
||||
ComponentName string
|
||||
// Name of application, the component is part of
|
||||
ApplicationName string
|
||||
// DevfilePath is the path of the devfile
|
||||
DevfilePath string
|
||||
// The path to the source of component(local or binary)
|
||||
Path string
|
||||
// List/Slice of files/folders in component source, the updates to which need not be pushed to component deployed pod
|
||||
FileIgnores []string
|
||||
StartOptions dev.StartOptions
|
||||
|
||||
// Custom function that can be used to push detected changes to remote pod. For more info about what each of the parameters to this function, please refer, pkg/component/component.go#PushLocal
|
||||
// WatchHandler func(kclient.ClientInterface, string, string, string, io.Writer, []string, []string, bool, []string, bool) error
|
||||
// Custom function that can be used to push detected changes to remote devfile pod. For more info about what each of the parameters to this function, please refer, pkg/devfile/adapters/interface.go#PlatformAdapter
|
||||
DevfileWatchHandler func(context.Context, adapters.PushParameters, WatchParameters, *ComponentStatus) error
|
||||
DevfileWatchHandler func(context.Context, common.PushParameters, *ComponentStatus) error
|
||||
// Parameter whether or not to show build logs
|
||||
Show bool
|
||||
// DevfileBuildCmd takes the build command through the command line and overwrites devfile build command
|
||||
DevfileBuildCmd string
|
||||
// DevfileRunCmd takes the run command through the command line and overwrites devfile run command
|
||||
DevfileRunCmd string
|
||||
// DevfileDebugCmd takes the debug command through the command line and overwrites the devfile debug command
|
||||
DevfileDebugCmd string
|
||||
// InitialDevfileObj is used to compare the devfile between the very first run of odo dev and subsequent ones
|
||||
InitialDevfileObj parser.DevfileObj
|
||||
// Debug indicates if the debug command should be started after sync, or the run command by default
|
||||
Debug bool
|
||||
// DebugPort indicates which debug port to use for pushing after sync
|
||||
DebugPort int
|
||||
// Variables override Devfile variables
|
||||
Variables map[string]string
|
||||
// RandomPorts is true to forward containers ports on local random ports
|
||||
RandomPorts bool
|
||||
// Optional: sCustomForwardedPorts configuration to be used to customize the port forwarding; if nil, we automatically select ports
|
||||
CustomForwardedPorts []api.ForwardedPort
|
||||
|
||||
// IgnoreLocalhost indicates whether to proceed with port-forwarding regardless of any container ports being bound to the container loopback interface.
|
||||
// Applicable to Podman only.
|
||||
IgnoreLocalhost bool
|
||||
// WatchFiles indicates to watch for file changes and sync changes to the container
|
||||
WatchFiles bool
|
||||
// ForwardLocalhost indicates whether to try to make port-forwarding work with container apps listening on the loopback interface.
|
||||
ForwardLocalhost bool
|
||||
// WatchCluster indicates to watch Cluster-related objects (Deployment, Pod, etc)
|
||||
WatchCluster bool
|
||||
// ErrOut is a Writer to output forwarded port information
|
||||
Out io.Writer
|
||||
// ErrOut is a Writer to output forwarded port information
|
||||
ErrOut io.Writer
|
||||
// PromptMessage
|
||||
PromptMessage string
|
||||
}
|
||||
@@ -118,14 +83,22 @@ type evaluateChangesFunc func(events []fsnotify.Event, path string, fileIgnores
|
||||
|
||||
// processEventsFunc processes the events received on the watcher. It uses the WatchParameters to trigger watch handler and writes to out
|
||||
// It returns a Duration after which to recall in case of error
|
||||
type processEventsFunc func(ctx context.Context, changedFiles, deletedPaths []string, parameters WatchParameters, out io.Writer, componentStatus *ComponentStatus, backoff *ExpBackoff) (*time.Duration, error)
|
||||
type processEventsFunc func(ctx context.Context, parameters WatchParameters, changedFiles, deletedPaths []string, componentStatus *ComponentStatus, backoff *ExpBackoff) (*time.Duration, error)
|
||||
|
||||
func (o *WatchClient) WatchAndPush(out io.Writer, parameters WatchParameters, ctx context.Context, componentStatus ComponentStatus) error {
|
||||
klog.V(4).Infof("starting WatchAndPush, path: %s, component: %s, ignores %s", parameters.Path, parameters.ComponentName, parameters.FileIgnores)
|
||||
func (o *WatchClient) WatchAndPush(ctx context.Context, parameters WatchParameters, componentStatus ComponentStatus) error {
|
||||
var (
|
||||
devfileObj = odocontext.GetDevfileObj(ctx)
|
||||
devfilePath = odocontext.GetDevfilePath(ctx)
|
||||
path = filepath.Dir(devfilePath)
|
||||
componentName = odocontext.GetComponentName(ctx)
|
||||
appName = odocontext.GetApplication(ctx)
|
||||
)
|
||||
|
||||
klog.V(4).Infof("starting WatchAndPush, path: %s, component: %s, ignores %s", path, componentName, parameters.StartOptions.IgnorePaths)
|
||||
|
||||
var err error
|
||||
if parameters.WatchFiles {
|
||||
o.sourcesWatcher, err = getFullSourcesWatcher(parameters.Path, parameters.FileIgnores)
|
||||
if parameters.StartOptions.WatchFiles {
|
||||
o.sourcesWatcher, err = getFullSourcesWatcher(path, parameters.StartOptions.IgnorePaths)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -138,7 +111,7 @@ func (o *WatchClient) WatchAndPush(out io.Writer, parameters WatchParameters, ct
|
||||
defer o.sourcesWatcher.Close()
|
||||
|
||||
if parameters.WatchCluster {
|
||||
selector := labels.GetSelector(parameters.ComponentName, parameters.ApplicationName, labels.ComponentDevMode, true)
|
||||
selector := labels.GetSelector(componentName, appName, labels.ComponentDevMode, true)
|
||||
o.deploymentWatcher, err = o.kubeClient.DeploymentWatcher(ctx, selector)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error watching deployment: %v", err)
|
||||
@@ -157,13 +130,13 @@ func (o *WatchClient) WatchAndPush(out io.Writer, parameters WatchParameters, ct
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if parameters.WatchFiles {
|
||||
if parameters.StartOptions.WatchFiles {
|
||||
var devfileFiles []string
|
||||
devfileFiles, err = libdevfile.GetReferencedLocalFiles(parameters.InitialDevfileObj)
|
||||
devfileFiles, err = libdevfile.GetReferencedLocalFiles(*devfileObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
devfileFiles = append(devfileFiles, parameters.DevfilePath)
|
||||
devfileFiles = append(devfileFiles, devfilePath)
|
||||
for _, f := range devfileFiles {
|
||||
err = o.devfileWatcher.Add(f)
|
||||
if err != nil {
|
||||
@@ -179,14 +152,14 @@ func (o *WatchClient) WatchAndPush(out io.Writer, parameters WatchParameters, ct
|
||||
return err
|
||||
}
|
||||
if isForbidden {
|
||||
log.Fwarning(out, "Unable to watch Events resource, warning Events won't be displayed")
|
||||
log.Fwarning(parameters.StartOptions.Out, "Unable to watch Events resource, warning Events won't be displayed")
|
||||
}
|
||||
} else {
|
||||
o.warningsWatcher = NewNoOpWatcher()
|
||||
}
|
||||
|
||||
o.keyWatcher = getKeyWatcher(ctx, out)
|
||||
return o.eventWatcher(ctx, parameters, out, evaluateFileChanges, o.processEvents, componentStatus)
|
||||
o.keyWatcher = getKeyWatcher(ctx, parameters.StartOptions.Out)
|
||||
return o.eventWatcher(ctx, parameters, evaluateFileChanges, o.processEvents, componentStatus)
|
||||
}
|
||||
|
||||
// eventWatcher loops till the context's Done channel indicates it to stop looping, at which point it performs cleanup.
|
||||
@@ -195,12 +168,19 @@ func (o *WatchClient) WatchAndPush(out io.Writer, parameters WatchParameters, ct
|
||||
func (o *WatchClient) eventWatcher(
|
||||
ctx context.Context,
|
||||
parameters WatchParameters,
|
||||
out io.Writer,
|
||||
evaluateChangesHandler evaluateChangesFunc,
|
||||
processEventsHandler processEventsFunc,
|
||||
componentStatus ComponentStatus,
|
||||
) error {
|
||||
|
||||
var (
|
||||
devfilePath = odocontext.GetDevfilePath(ctx)
|
||||
path = filepath.Dir(devfilePath)
|
||||
componentName = odocontext.GetComponentName(ctx)
|
||||
appName = odocontext.GetApplication(ctx)
|
||||
out = parameters.StartOptions.Out
|
||||
)
|
||||
|
||||
expBackoff := NewExpBackoff()
|
||||
|
||||
var events []fsnotify.Event
|
||||
@@ -245,7 +225,7 @@ func (o *WatchClient) eventWatcher(
|
||||
var changedFiles, deletedPaths []string
|
||||
if !o.forceSync {
|
||||
// first find the files that have changed (also includes the ones newly created) or deleted
|
||||
changedFiles, deletedPaths = evaluateChangesHandler(events, parameters.Path, parameters.FileIgnores, o.sourcesWatcher)
|
||||
changedFiles, deletedPaths = evaluateChangesHandler(events, path, parameters.StartOptions.IgnorePaths, o.sourcesWatcher)
|
||||
// process the changes and sync files with remote pod
|
||||
if len(changedFiles) == 0 && len(deletedPaths) == 0 {
|
||||
continue
|
||||
@@ -254,7 +234,7 @@ func (o *WatchClient) eventWatcher(
|
||||
|
||||
componentStatus.State = StateSyncOutdated
|
||||
fmt.Fprintf(out, "Pushing files...\n\n")
|
||||
retry, err := processEventsHandler(ctx, changedFiles, deletedPaths, parameters, out, &componentStatus, expBackoff)
|
||||
retry, err := processEventsHandler(ctx, parameters, changedFiles, deletedPaths, &componentStatus, expBackoff)
|
||||
o.forceSync = false
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -292,7 +272,7 @@ func (o *WatchClient) eventWatcher(
|
||||
}
|
||||
|
||||
case <-deployTimer.C:
|
||||
retry, err := processEventsHandler(ctx, nil, nil, parameters, out, &componentStatus, expBackoff)
|
||||
retry, err := processEventsHandler(ctx, parameters, nil, nil, &componentStatus, expBackoff)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -308,7 +288,7 @@ func (o *WatchClient) eventWatcher(
|
||||
|
||||
case <-devfileTimer.C:
|
||||
fmt.Fprintf(out, "Updating Component...\n\n")
|
||||
retry, err := processEventsHandler(ctx, nil, nil, parameters, out, &componentStatus, expBackoff)
|
||||
retry, err := processEventsHandler(ctx, parameters, nil, nil, &componentStatus, expBackoff)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -320,7 +300,7 @@ func (o *WatchClient) eventWatcher(
|
||||
}
|
||||
|
||||
case <-retryTimer.C:
|
||||
retry, err := processEventsHandler(ctx, nil, nil, parameters, out, &componentStatus, expBackoff)
|
||||
retry, err := processEventsHandler(ctx, parameters, nil, nil, &componentStatus, expBackoff)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -351,7 +331,7 @@ func (o *WatchClient) eventWatcher(
|
||||
switch kevent := ev.Object.(type) {
|
||||
case *corev1.Event:
|
||||
podName := kevent.InvolvedObject.Name
|
||||
selector := labels.GetSelector(parameters.ComponentName, parameters.ApplicationName, labels.ComponentDevMode, true)
|
||||
selector := labels.GetSelector(componentName, appName, labels.ComponentDevMode, true)
|
||||
matching, err := o.kubeClient.IsPodNameMatchingSelector(ctx, podName, selector)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -438,12 +418,17 @@ func evaluateFileChanges(events []fsnotify.Event, path string, fileIgnores []str
|
||||
|
||||
func (o *WatchClient) processEvents(
|
||||
ctx context.Context,
|
||||
changedFiles, deletedPaths []string,
|
||||
parameters WatchParameters,
|
||||
out io.Writer,
|
||||
changedFiles, deletedPaths []string,
|
||||
componentStatus *ComponentStatus,
|
||||
backoff *ExpBackoff,
|
||||
) (*time.Duration, error) {
|
||||
var (
|
||||
devfilePath = odocontext.GetDevfilePath(ctx)
|
||||
path = filepath.Dir(devfilePath)
|
||||
out = parameters.StartOptions.Out
|
||||
)
|
||||
|
||||
for _, file := range removeDuplicates(append(changedFiles, deletedPaths...)) {
|
||||
fmt.Fprintf(out, "\nFile %s changed\n", file)
|
||||
}
|
||||
@@ -452,22 +437,14 @@ func (o *WatchClient) processEvents(
|
||||
|
||||
klog.V(4).Infof("Copying files %s to pod", changedFiles)
|
||||
|
||||
pushParams := adapters.PushParameters{
|
||||
Path: parameters.Path,
|
||||
pushParams := common.PushParameters{
|
||||
StartOptions: parameters.StartOptions,
|
||||
WatchFiles: changedFiles,
|
||||
WatchDeletedFiles: deletedPaths,
|
||||
IgnoredFiles: parameters.FileIgnores,
|
||||
DevfileBuildCmd: parameters.DevfileBuildCmd,
|
||||
DevfileRunCmd: parameters.DevfileRunCmd,
|
||||
DevfileDebugCmd: parameters.DevfileDebugCmd,
|
||||
DevfileScanIndexForWatch: !hasFirstSuccessfulPushOccurred,
|
||||
Debug: parameters.Debug,
|
||||
RandomPorts: parameters.RandomPorts,
|
||||
CustomForwardedPorts: parameters.CustomForwardedPorts,
|
||||
ErrOut: parameters.ErrOut,
|
||||
}
|
||||
oldStatus := *componentStatus
|
||||
err := parameters.DevfileWatchHandler(ctx, pushParams, parameters, componentStatus)
|
||||
err := parameters.DevfileWatchHandler(ctx, pushParams, componentStatus)
|
||||
if err != nil {
|
||||
if isFatal(err) {
|
||||
return nil, err
|
||||
@@ -485,7 +462,7 @@ func (o *WatchClient) processEvents(
|
||||
fmt.Fprintf(out, "Updated Kubernetes config\n")
|
||||
}
|
||||
} else {
|
||||
if parameters.WatchFiles {
|
||||
if parameters.StartOptions.WatchFiles {
|
||||
fmt.Fprintf(out, "%s - %s\n\n", PushErrorString, err.Error())
|
||||
} else {
|
||||
return nil, err
|
||||
@@ -498,7 +475,7 @@ func (o *WatchClient) processEvents(
|
||||
if oldStatus.State != StateReady && componentStatus.State == StateReady ||
|
||||
!reflect.DeepEqual(oldStatus.EndpointsForwarded, componentStatus.EndpointsForwarded) {
|
||||
|
||||
PrintInfoMessage(out, parameters.Path, parameters.WatchFiles, parameters.PromptMessage)
|
||||
PrintInfoMessage(out, path, parameters.StartOptions.WatchFiles, parameters.PromptMessage)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@@ -563,5 +540,5 @@ func PrintInfoMessage(out io.Writer, path string, watchFiles bool, promptMessage
|
||||
}
|
||||
|
||||
func isFatal(err error) bool {
|
||||
return errors.As(err, &adapters.ErrPortForward{})
|
||||
return errors.As(err, &common.ErrPortForward{})
|
||||
}
|
||||
|
||||
@@ -4,13 +4,15 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/dev"
|
||||
odocontext "github.com/redhat-developer/odo/pkg/odo/context"
|
||||
)
|
||||
|
||||
func evaluateChangesHandler(events []fsnotify.Event, path string, fileIgnores []string, watcher *fsnotify.Watcher) ([]string, []string) {
|
||||
@@ -40,8 +42,8 @@ func evaluateChangesHandler(events []fsnotify.Event, path string, fileIgnores []
|
||||
return changedFiles, deletedPaths
|
||||
}
|
||||
|
||||
func processEventsHandler(ctx context.Context, changedFiles, deletedPaths []string, _ WatchParameters, out io.Writer, componentStatus *ComponentStatus, backo *ExpBackoff) (*time.Duration, error) {
|
||||
fmt.Fprintf(out, "changedFiles %s deletedPaths %s\n", changedFiles, deletedPaths)
|
||||
func processEventsHandler(ctx context.Context, params WatchParameters, changedFiles, deletedPaths []string, componentStatus *ComponentStatus, backo *ExpBackoff) (*time.Duration, error) {
|
||||
fmt.Fprintf(params.StartOptions.Out, "changedFiles %s deletedPaths %s\n", changedFiles, deletedPaths)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -89,7 +91,11 @@ func Test_eventWatcher(t *testing.T) {
|
||||
{
|
||||
name: "Case 3: Delete file, no error",
|
||||
args: args{
|
||||
parameters: WatchParameters{FileIgnores: []string{"file1"}},
|
||||
parameters: WatchParameters{
|
||||
StartOptions: dev.StartOptions{
|
||||
IgnorePaths: []string{"file1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantOut: "Pushing files...\n\nchangedFiles [] deletedPaths [file1 file2]\n",
|
||||
wantErr: true,
|
||||
@@ -113,6 +119,9 @@ func Test_eventWatcher(t *testing.T) {
|
||||
fileWatcher, _ := fsnotify.NewWatcher()
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx = odocontext.WithDevfilePath(ctx, "/path/to/devfile")
|
||||
ctx = odocontext.WithApplication(ctx, "odo")
|
||||
ctx = odocontext.WithComponentName(ctx, "my-component")
|
||||
out := &bytes.Buffer{}
|
||||
|
||||
go func() {
|
||||
@@ -139,7 +148,9 @@ func Test_eventWatcher(t *testing.T) {
|
||||
devfileWatcher: fileWatcher,
|
||||
keyWatcher: make(chan byte),
|
||||
}
|
||||
err := o.eventWatcher(ctx, tt.args.parameters, out, evaluateChangesHandler, processEventsHandler, componentStatus)
|
||||
tt.args.parameters.StartOptions.Out = out
|
||||
|
||||
err := o.eventWatcher(ctx, tt.args.parameters, evaluateChangesHandler, processEventsHandler, componentStatus)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("eventWatcher() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user