-
Notifications
You must be signed in to change notification settings - Fork 244
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Docker Devfile Component Sync and Execution #2922
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall changes look good, left some preliminary comments based on your WIP changes for sync, I'll re-review once exec is in too.
Will you be adding unit tests to this PR?
pkg/lclient/containers.go
Outdated
@@ -83,3 +87,80 @@ func (dc *Client) GetContainerConfig(containerID string) (*container.Config, err | |||
|
|||
return containerJSON.Config, nil | |||
} | |||
|
|||
//ExecCMDInContainer executes | |||
func (dc *Client) ExecCMDInContainer(podName string, containerID string, cmd []string, stdout io.Writer, stderr io.Writer, stdin io.Reader, tty bool) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand why podName string
is an arg here (because of the SyncClient interface), but...
We should consider changing the SyncClient
interface to take a function like ExecCMDInComponent
instead of ExecCMDinContainer
, then the client's function (either local or Kube) determines the container (local) or Pod (kube) that needs to be exec'd into. That way we don't need to pass in a (blank) pod name in the Docker client code.
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addressed in 162657e
pkg/lclient/containers.go
Outdated
AttachStdout: stdout != nil, | ||
AttachStderr: stderr != nil, | ||
Cmd: cmd, | ||
// WorkingDir: "/tmp", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this commented out param be removed?
if err != nil { | ||
return err | ||
} | ||
} | ||
} | ||
} else { | ||
return recursiveTar(filepath.Dir(srcPath), filepath.Base(srcPath), filepath.Dir(destPath), filepath.Base(destPath), tarWriter, globExps) | ||
return recursiveTar(filepath.Dir(srcPath), filepath.Base(srcPath), filepath.Dir(destPath), "", tarWriter, globExps) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you change the param for destFile
from filepath.Base(destPath)
to ""
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So odo 1 currently tar'd local projects files on your laptop/workspace to /projects
in the container, but it did in a way that the archvie had a top level dir. For example, if your local laptop current dir was someproject
where you ran odo push, then tar would create someproject/<files>
and did a tar extract with command cmdArr := []string{"tar", "xf", "-", "-C", targetPath, "--strip", "1"}
where strip was basically stripping this top level dir created during tar, so that inside the container, files would be in /projects
This was fine with kube exec api, but docker client api CopyToContainer
does not handle this. The files are copied to /projects/someproject/<files>
and would need to execute another command to handle this.
So instead of creating the destination tar in filepath.Base(destPath)
, we just do not specify a dir, and pass in ""
for this reason and tar extract command has been updated to remove the strip option in kclient/occlient implementation
) | ||
|
||
// New instantiantes a component adapter | ||
func New(adapterContext common.AdapterContext, client SyncClient) Adapter { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the benefit of an adapter here over just adding functions to a SyncClient
instance?
I'm fine with either approach here, just curious.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just thought it was cleaner, but yeah i can remove the adapter and perhaps make them functions like the other packages
pkg/sync/adapter.go
Outdated
if !podChanged && !parameters.ForceBuild && len(parameters.WatchFiles) == 0 && len(parameters.WatchDeletedFiles) == 0 { | ||
absIgnoreRules := util.GetAbsGlobExps(parameters.Path, parameters.IgnoredFiles) | ||
|
||
spinner := log.NewStatus(log.GetStdout()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you use log.Spinner()
for the spinner here? To be consistent with the rest of odo. And pair that with a defer s.End(false)
// GetSupervisordVolumeLabels returns the label selectors used to retrieve the supervisord volume | ||
func GetSupervisordVolumeLabels() map[string]string { | ||
supervisordLabels := map[string]string{ | ||
"name": adaptersCommon.SupervisordVolumeName, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since there be multiple versions of supervisord, could we make one of the labels the version of supervisord? So that old versions of the supervisord volume aren't accidentally used by odo
adaptersCommon.SupervisordMountPath, | ||
} | ||
|
||
s := log.Spinner("Pulling image " + image) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe put this log behind a glog.v(3)
? Don't think the user needs to see it by default
) | ||
// Get a sync adapter. Check if project files have changed and sync accordingly | ||
syncAdapter := sync.New(a.AdapterContext, &a.Client) | ||
err = syncAdapter.CheckProjectFiles(parameters, pod.GetName(), containerName, podChanged, componentExists) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we change the name of CheckProjectFiles
to be something more clear? Something like SyncFiles
?
4a9f018
to
2aed578
Compare
pkg/devfile/adapters/common/utils.go
Outdated
// if there are two commands, it is the optional build command and the mandatory run command, set buildRequired to true | ||
buildRequired = true | ||
} else { | ||
return false, fmt.Errorf("error executing devfile commands - there should be at least 1 command or at most 2 commands, currently there are %v commands", len(pushDevfileCommands)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will this error cause issues when we need to add support devfile init commands (or other commads) later for Docker? Would it be better to just set buildRequired = true
if len(pushDevfileCommands) >= 2
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dev init PR changed the logic, which i think is better(glancing at it). That will replace this..
@@ -36,6 +68,29 @@ func (a Adapter) Push(parameters common.PushParameters) (err error) { | |||
return errors.Wrap(err, "unable to create or update component") | |||
} | |||
|
|||
containers := utils.GetComponentContainers(a.Client, a.ComponentName) | |||
// Find at least one pod with the source volume mounted, error out if none can be found |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change pod
to container
😉
// Find at least one pod with the source volume mounted, error out if none can be found | ||
containerID, err := getFirstContainerWithSourceVolume(containers) | ||
if err != nil { | ||
return errors.Wrapf(err, "error while retrieving container for component: %s", a.ComponentName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you change the error message to be a little more clear that we couldn't find any volume that had the source volume mounted on it?
syncAdapter := sync.New(a.AdapterContext, &a.Client) | ||
// podName is set to empty string on docker | ||
// podChanged is set to false, since docker volume is always present even if container goes down | ||
isPushRequired, err := syncAdapter.SyncFiles(parameters, "", containerID, false, componentExists) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SyncFiles
takes in a number of params, could we merge them into a single struct like we did for the push params? Then we wouldn't have to explicitly set an empty podName
for Docker. Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you change isPushRequired
to something else, since part of what SyncDoes
is push the changes to the running container. Maybe something like buildRequired
or execRequired
?
pkg/sync/adapter.go
Outdated
common.AdapterContext | ||
} | ||
|
||
// SyncFiles checks whether files have changed in a project |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function description should be updated, as it doesn't just check if the files changed:
- If files are passed in from watch, it syncs those files directly
- Otherwise, it determines which files changed and then syncs them to the conainer
return supervisordLabels | ||
} | ||
|
||
// CreateAndInitSupervisordVolume creates the supervisord volume and initializes it with odo init |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean by initializes it with odo init
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated to say supervisord bootstrap, i meant odo init repo earlier
} | ||
|
||
var s *log.Status | ||
if log.IsDebug() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As an alternative, we can do if glog.V(4) {
so that it shows up properly when users do odo push --v <level>
(see https://github.com/openshift/odo/blob/master/pkg/lclient/images.go#L24)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, think will stick with log.IsDebug()
since it returns true for any -v
> 0, then its not just bounded by -v 4
And we also use log.IsDebug()
for showing build output from build command
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough, works for me.
AddVolumeToComp(supervisordVolumeName, adaptersCommon.SupervisordMountPath, &hostConfig) | ||
|
||
// Create the docker container | ||
if log.IsDebug() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See my other comment about using if glog.V(4)
err = a.execDevfile(pushDevfileCommands, componentExists, parameters.Show, pod.GetName(), pod.Spec.Containers) | ||
if err != nil { | ||
return err | ||
if isPushRequired { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See other comment about renaming isPushRequired
pkg/exec/devfile.go
Outdated
|
||
err := ExecuteCommand(client, podName, containerName, cmdArr, show) | ||
if err != nil { | ||
s.End(false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because of the defer s.End(false)
, we don't need the s.End(false)
here.
/refresh |
@maysunfaisal Need to rebase / resolve conflicts. Looks like a lot of code I was going to review is either going to be overridden or changed due to the conflicts. |
@cdrage it would be nice if you could look at the sync code changes, they have changed a teeny bit and they dont have merge conflicts. This affects occlient, kclient and lcient - See review discussion - #2922 (comment) |
/retest |
There's still conflicts :( I don't think you pushed the changes? |
I don't see any integrations test that would verify that the commands were actually executed, and the application was build and started :-/ |
/approve |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: kadel The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
/retest Please review the full test history for this PR and help us cut down flakes. |
Signed-off-by: Maysun J Faisal <maysun.j.faisal@ibm.com>
Signed-off-by: Maysun J Faisal <maysun.j.faisal@ibm.com>
Signed-off-by: Maysun J Faisal <maysun.j.faisal@ibm.com>
Signed-off-by: Maysun J Faisal <maysun.j.faisal@ibm.com>
Signed-off-by: Maysun J Faisal <maysun.j.faisal@ibm.com>
Signed-off-by: Maysun J Faisal <maysun.j.faisal@ibm.com>
Signed-off-by: Maysun J Faisal <maysun.j.faisal@ibm.com>
Signed-off-by: Maysun J Faisal <maysun.j.faisal@ibm.com>
Signed-off-by: Maysun J Faisal <maysun.j.faisal@ibm.com>
Signed-off-by: Maysun J Faisal <maysun.j.faisal@ibm.com>
Signed-off-by: Maysun J Faisal <maysun.j.faisal@ibm.com>
Signed-off-by: Maysun J Faisal <maysun.j.faisal@ibm.com>
Signed-off-by: Maysun J Faisal <maysun.j.faisal@ibm.com>
Signed-off-by: Maysun J Faisal <maysun.j.faisal@ibm.com>
Signed-off-by: Maysun J Faisal <maysun.j.faisal@ibm.com>
Signed-off-by: Maysun J Faisal <maysun.j.faisal@ibm.com>
Signed-off-by: Maysun J Faisal <maysun.j.faisal@ibm.com>
/lgtm |
/retest |
Tests all pass but we're waiting for benchmarks to complete. So merging manually. |
What type of PR is this?
/kind feature
What does does this PR do / why we need it:
This PR adds functionality to sync project and exec devfile commands for the docker component.
Specifically, this PR:
/projects
for components that has mount sources trueWhich issue(s) this PR fixes:
#2717
How to test changes / Special notes to the reviewer:
odo preference set pushtarget docker
odo create <component>
odo push