-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Add Exec
to the gateway API.
#749
Comments
To clarify the intent here, if you also added a feature to connect the Exec'd container's stdio to a pty, would this issue enable an "interactive" container? I.e. a container specified by LLB that spawns a usable shell/editor/other TTY-based program? |
Thinking about it more, I think we definitely need the ability for the user to interact with a build result like a regular container. This is needed for debug scenarios. Maybe we don't necessarily need the frontend to have this access? And if frontend does use |
We previously started the conversation on Slack, but I'm paraphrasing it here to save the ideas somewhere. cc @coryb
Scenario 1: Exec from regular LLBexecState := llb.Image("busybox").Run(llb.Shlex("/bin/sh"))
// Approach #1a
p := execState.ToExecProcess()
p.Run(ctx, io, tty)
// Appraoch #1b
execState.Run(ctx, io, tty) Scenario 2: Exec into failed solve resultvar st llb.State
def, _ := st.Marshal(ctx)
res, err := c.Solve(ctx, client.SolveRequest{Definition: def.ToPB()})
if err != nil {
var er *ExecError
if errors.As(err, &er) {
// Approach #2a
c.Exec(ctx, er.Meta(), er.ErrorStateMounts(), io, tty)
// Approach #2b
p := ToExecProcess(er)
p.Run(ctx, io, tty)
}
} Note that Approach #2b still needs Approach #2a to be implemented, #2b is an object-oriented UX slapped ontop of #2a. How can we make this happen? (Ongoing list)
|
I am hoping we can agree on a As I see it we will need 2 grpc components:
1 - Exec RPCI think something like this will work: service LLBBridge {
rpc Exec(ExecRequest) returns (ExecResponse)
}
message ExecRequest {
pb.ExecOp op = 1;
repeated uint32 openFds = 2;
}
message ExecResponse {
// What goes here? Anything? Task Exit Status?
} I think the message Meta {
repeated string args = 1;
repeated string env = 2;
string cwd = 3;
string user = 4;
ProxyEnv proxy_env = 5;
repeated HostIP extraHosts = 6;
bool tty = 7;
} since that matches the structure already available in the 2 - IO bridge serviceWe need some way to stream input/output to the container task. I suggest creating a grpc service on the client side that can be accessed from the server via the session manager, similar to the Attachable SSHForwarder implementation. This is the service IOBridge {
rpc IO(stream FdMessage) returns (stream IOInput)
}
message IOInput {
oneof input {
FdMessage file = 1;
ResizeMessage resize = 2;
}
}
message FdMessage{
uint32 fd = 1; // what fd the data was from
bool eof = 2; // true if eof was reached
bytes data = 3;
}
message ResizeMessage{
uint32 rows = 1;
uint32 cols = 2;
uint32 xpixel = 3;
uint32 ypixel = 4;
} Thoughts? Adjustments? Counter proposals? |
how about service LLBBridge {
rpc NewContainer(NewContainerRequest) returns (NewContainerResponse);
rpc ReleaseContainer(ReleaseContainerRequest) returns (ReleaseContainerResponse);
rpc ExecProcess(stream ExecMessage) returns (stream ExecMessage);
}
message NewContainerRequest {
string Ref = 1;
// For mount input values we can use random identifiers passed with ref
repeated pb.Mount mounts = 2;
pb.NetMode network = 3;
pb.SecurityMode security = 4
}
message ReleaseContainerRequest {
string Ref = 1;
}
message ExecMessage {
oneof input {
InitMessage init = 1;
FdMessage file = 2;
ResizeMessage resize = 3;
StartedMessage started = 4;
ExitMessage exit = 5;
}
}
message InitMessage{
pb.Meta Meta = 1;
repeated uint32 fds = 2;
bool tty = 3;
// ?? way to control if this is PID1? probably not needed
}
message ExitMessage {
uint32 Code = 1;
}
message FdMessage{
uint32 fd = 1; // what fd the data was from
bool eof = 2; // true if eof was reached
bytes data = 3;
}
message ResizeMessage{
uint32 rows = 1;
uint32 cols = 2;
uint32 xpixel = 3;
uint32 ypixel = 4;
} I added "container" concept atm to support multiple exec processes. Not sure if needed initially but probably better to be safe for future ideas. The complex part of this is that it does not allow reusing the current executor directly, eg. runc needs to be invoked with create/start/exec calls instead of a single run. Or we mark one process as pid1 and then I think we can use run+exec. Sending |
These are the changes in the
Some investigation is needed if both runs and contained support Outline:
I think the executor changes can be done in a separate PR before the changes in proto API with some unit tests to ease the review. |
Runc vs Containerd questionsThese seems to be the relevant questions that might influence implementation: Runca) No, config.json must have Containerda) Yes, you do not have to explicitly define a pid1 (will assume defaults from image) ProposalSo it seems for a common implementation we will have to assume "No" to all these questions. After some discussion with @hinshun this seems like a reasonable approach: => NewContainer(ref, rootfs, mounts, network, security) message
=> ExecProcess.InitMessage(ref, meta, IO) message
|
@coryb LGTM |
How do we specify the inputs for
|
For the client side, afaict it seems like we need to modify the I was thinking something like this:
The
Does this seem like the right approach? |
On further thinking, even though the code path for each process is similar, I think we need to separate the pid1 vs exec since if pid1 dies all the exec process will also die. To be consistent with the gateway api, maybe rename
It also seems like return |
This looks good to me. I would just change |
I'm not sure where to draw the boundary between llb cliennt and gateway client packages. Maybe gateway client should mostly copy the protobuf methods. Except instead of using the mount ID use the The reason for having the newcontainer without starting the process was that there could be a synchronization point between build and starting the process. Eg. this is important for switching client stdio from buildkit progressbar to the attached process. I'd use the same For the API that users should use I'd expect more user-friendliness. Eg.
Previously we also discussed |
I think a minimal protobuf-ish api would look something like:
Not sure we can get much more minimal than that. To populate the new
So gateway client code would roughly look something like:
|
I was also thinking we could add any more user friendly wrappers to |
Actually, looking at that example I don't think it makes sense to create wrapping (except ExecState) to gateway.client as that what user has access to. I'd still also prefer just using the ref object directly and leaving the string ID hidden. wdyt?
|
Okay, works for me. So this is what we are looking at adding to the
Looking at the two options for process management, I am leaning towards option |
@coryb sgtm Misssing release on container:
With B we could add sending signals later. Could also do resize as a method there. Unless you want to reuse the type from |
@tonistiigi Yeah, I was originally thinking to use the executor.ProcessInfo directly to deal with io and resize, but now I am leaning towards adding Last question (for now): Should I add these changes to #1627 along with tests? Or we can merge #1627 without tests (I think I need these gateway client updates to effectively test the server side) and I create a new PR for client + tests? |
I guess depends on how long the client-side takes. #1627 would be somewhat easier to review if there is some code actually calling the new functions. |
Sounds good, I will update #1627, should be able to get it done next week. |
#1627 is merged and follow-up issues have been opened separately so closing this. |
gw.Exec
should invoke a command similarily to thefrontend.Exec
does atm. The main difference fromSolve
is thatExec
is never cached. Should be able to take the same arguments thatllb.Exec
takes (including the special mounts). More complex cases for things likeReadFile
could be implemented withExec
.The mounts of
Exec
should takeReferences
from completed sources as inputs.The text was updated successfully, but these errors were encountered: