-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Less interfaces #3316
Less interfaces #3316
Conversation
43300a7
to
a3bc8cc
Compare
088c13b
to
213a682
Compare
No longer a draft; if others agree, this can be merged right after 1.1 |
// If the Container state is RUNNING or CREATED, sets the Container | ||
// state to PAUSING and pauses the execution of any user processes. |
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.
// If the Container state is RUNNING or CREATED, sets the Container | |
// state to PAUSING and pauses the execution of any user processes. | |
// Pause sets the Container state to PAUSING and pauses the execution of any | |
// user processes if the Container state is RUNNING or CREATED. |
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 this commit did is
Remove BaseContainer and Container interfaces, moving their methods
documentation to linuxContainer.
I was not changing any comments.
Also, it seems that the doc is wrong and the async part and "pausing" state was removed long time ago (I was not able to find it being used in git history).
Let's address this separately (on top of this PR once it's merged). Opened #3343 so we won't forget.
@@ -35,7 +35,7 @@ import ( | |||
|
|||
const stdioFdCount = 3 | |||
|
|||
type linuxContainer struct { | |||
type Container struct { |
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.
Perhaps copy the previous docs here?
type Container struct { | |
// Container is a libcontainer container object. | |
// | |
// Each container is thread-safe within the same process. Since a container can | |
// be destroyed by a separate process, any function may return that the container | |
// was not found. | |
type Container struct { |
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.
Frankly, I do not understand the part about thread-safeness, and the rest is kind of obvious.
utils_linux.go
Outdated
if err != nil { | ||
newuidmap = "" | ||
if newuidmap, err := exec.LookPath("newuidmap"); err == nil { | ||
f.NewuidmapPath = newuidmap |
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.
TBH; not sure I like this direction; with these changes, it looks like loadFactory()
becomes the actual constructor, and not New()
. I feel like either New()
should do the constructing, or we should remove New()
altogether if it's not really a constructor
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.
Yes, I guess I was going that way but fell short of the last step.
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.
OK, with a few more commits here and there I think I did it. PTAL @thaJeztah
The only question is, should we call what's left of it a factory? I can certainly rename it but can't think of any good name -- suggestions welcome.
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.
Almost tempted to say; perhaps it should be named LibContainer
(as it's constructed using libcontainer.New()
🤔
Even further; I'm even starting to wonder if libcontainer.New()
should be public at all. All cases except for getContainers()
("list containers"), basically follow the pattern;
factory := libcontainer.New()
factory.Create()
/factory.Load()
- done
It would almost make sense to just libcontainer.Create()
, libcontainer.Get()
, libcontainer.List()
(which internally can do a new()
), adding root
as argument.
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.
It would almost make sense to just libcontainer.Create(), libcontainer.Get(), libcontainer.List() (which internally can do a new()), adding root as argument.
This makes great sense (and we can get rid of factory
entirely, so we don't have to think about naming it!).
e718e3c
to
34afdfa
Compare
34afdfa
to
429dff9
Compare
Rebased; PTAL @thaJeztah |
It's clear at this point that runc won't support Windows. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
Since the next commit is going to touch this structure, our CI (lint-extra) is about to complain about improperly named field: > Warning: var-naming: struct field ContainerId should be ContainerID (revive) Make it happy. Brought to use by gopls rename. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
StartInitialization does not have to be a method of Factory (while it is clear why it was done that way initially, now we only have Linux containers so it does not make sense). Fix callers and docs accordingly. No change in functionality. Also, since this was the only user of libcontainer.New with the empty string as an argument, the corresponding check can now be removed from it. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
Currently, TestInit sets up logrus, and init uses it to log an error from StartInitialization(). This is solely used by TestExecInError to check that error returned from StartInitialization is the one it expects. Note that the very same error is communicated to the runc init parent and is ultimately returned by container.Run(), so checking what StartInitialization returned is redundant. Remove logrus setup and use from TestMain/init, in preparation for the next commit. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
Note that since this is now also called from libcontainer/integration, which do not set _LIBCONTAINER_LOGLEVEL, make it optional. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
> libcontainer/factory.go#L245 > var-naming: var fdsJson should be fdsJSON (revive) and > libcontainer/init_linux.go#L181 > error-strings: error strings should not be capitalized or end with punctuation or a newline (revive) and > notify_socket.go#L94 > receiver-naming: receiver name n should be consistent with previous receiver name s for notifySocket (revive) Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
Instead of having newContainerInit return an interface, and let its caller call Init(), it is easier to call Init directly. Do that, and rename newContainerInit to containerInit. I think it makes the code more readable and straightforward. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
Those are *always* /proc/self/exe init, and it does not make sense to change these (in case there ever will be, I guess, it can be addressed separately). Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
We only have one implementation of config validator, which is always used. It makes no sense to have Validator interface. Having validate.Validator field in Factory does not make sense for all the same reasons. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
These were introduced in commit d8b6694 back in 2017, with a TODO of "make binary names configurable". Apparently, everyone is happy with the hardcoded names. In fact, they *are* configurable (by prepending the PATH with a directory containing own version of newuidmap/newgidmap). Now, these binaries are only needed in a few specific cases (when rootless is set etc.), so let's loop them up when needed. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
All its users were removed. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
This was introduced in an initial commit, and is not really needed for most of the users, since they rely on CRIU binary provided by their distro. Now, if someone needs a custom criu binary to be used, they can put it into some directory and make sure this directory is listed first in $PATH. Make --criu a hidden option (thus removing it from help). Remove it from man pages, integration tests, etc. Remove all traces of CriuPath from data structures. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
- move filepath.Abs to libcontainer.New; - remove loadFactory. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
429dff9
to
3d5fd4a
Compare
This PR lacked needed documentation changes in libcontainer/README. Added now. |
@adrianreber can you please review this single commit and tell us WDYT? |
I don't like it 😉 This is probably only an interface used by people adding new CRIU features to runc like me, but I use it regularly. I am also using it in my (hopefully) upcoming CRI-O checkpoint/restore implementation. It is strange to have it at the main command level, it could also work as a checkpoint or restore subcommand option. I am probably the only user of it but I like it. I will manage without that flag, but I would prefer to keep it. |
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.
made another pass at reviewing; looks like it needs a rebase (sorry!)
overall looking good; left some suggestions (happy to discuss)
type Capabilities struct { | ||
// Bounding is the set of capabilities checked by the kernel. | ||
Bounding []string | ||
// Effective is the set of capabilities checked by the kernel. | ||
Effective []string | ||
// Inheritable is the capabilities preserved across execve. | ||
Inheritable []string | ||
// Permitted is the limiting superset for effective capabilities. | ||
Permitted []string | ||
// Ambient is the ambient set of capabilities that are kept. | ||
Ambient []string | ||
} |
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 don't think it will cause any issues, but mentioninging it (just in case); the type is the same from a "go" perspective, but the runtime-spec type uses lowercase names (and omitempty
) for the JSON representation; https://github.com/opencontainers/runtime-spec/blob/a3c33d663ebc56c4d35dbceaa447c7bf37f6fab3/specs-go/config.go#L65-L78
// LinuxCapabilities specifies the list of allowed capabilities that are kept for a process.
// http://man7.org/linux/man-pages/man7/capabilities.7.html
type LinuxCapabilities struct {
// Bounding is the set of capabilities checked by the kernel.
Bounding []string `json:"bounding,omitempty" platform:"linux"`
// Effective is the set of capabilities checked by the kernel.
Effective []string `json:"effective,omitempty" platform:"linux"`
// Inheritable is the capabilities preserved across execve.
Inheritable []string `json:"inheritable,omitempty" platform:"linux"`
// Permitted is the limiting superset for effective capabilities.
Permitted []string `json:"permitted,omitempty" platform:"linux"`
// Ambient is the ambient set of capabilities that are kept.
Ambient []string `json:"ambient,omitempty" platform:"linux"`
}
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.
Well, this might open the hell gates:
[root@kir-rhat runc]# jq < /run/runc/xxx/state.json | grep -A10 capab
"capabilities": {
"Bounding": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"Effective": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
[root@kir-rhat runc]# jq < /run/runc/xx344/state.json | grep -A10 capab
"capabilities": {
"bounding": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"effective": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
From what I can see, this is not used anywhere, and yet it might become problematic.
I guess I'll revert this.
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.
If these files are only used by runc and/or only used by Go code, then it shouldn't (AFAICS) be an issue. json.Unmarshal()
is case insensitive for keys (for better or worse 😅)
https://go.dev/play/p/9bo-nSvEARD
package main
import (
"encoding/json"
"fmt"
"reflect"
)
type Capabilities struct {
// Bounding is the set of capabilities checked by the kernel.
Bounding []string
// Effective is the set of capabilities checked by the kernel.
Effective []string
// Inheritable is the capabilities preserved across execve.
Inheritable []string
// Permitted is the limiting superset for effective capabilities.
Permitted []string
// Ambient is the ambient set of capabilities that are kept.
Ambient []string
}
type LinuxCapabilities struct {
// Bounding is the set of capabilities checked by the kernel.
Bounding []string `json:"bounding,omitempty" platform:"linux"`
// Effective is the set of capabilities checked by the kernel.
Effective []string `json:"effective,omitempty" platform:"linux"`
// Inheritable is the capabilities preserved across execve.
Inheritable []string `json:"inheritable,omitempty" platform:"linux"`
// Permitted is the limiting superset for effective capabilities.
Permitted []string `json:"permitted,omitempty" platform:"linux"`
// Ambient is the ambient set of capabilities that are kept.
Ambient []string `json:"ambient,omitempty" platform:"linux"`
}
type runc struct {
Capabilities *Capabilities `json:"capabilities"`
}
type oci struct {
Capabilities *LinuxCapabilities `json:"capabilities"`
}
const jsonLower = `
{
"capabilities": {
"bounding": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"effective": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
]
}
}`
const jsonUpper = `
{
"capabilities": {
"Bounding": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"Effective": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
]
}
}`
func main() {
var (
runcLower runc
runcUpper runc
ociLower oci
ociUpper oci
)
if err := json.Unmarshal([]byte(jsonLower), &runcLower); err != nil {
fmt.Println(err)
}
if err := json.Unmarshal([]byte(jsonUpper), &runcUpper); err != nil {
fmt.Println(err)
}
if err := json.Unmarshal([]byte(jsonLower), &ociLower); err != nil {
fmt.Println(err)
}
if err := json.Unmarshal([]byte(jsonUpper), &ociUpper); err != nil {
fmt.Println(err)
}
if reflect.DeepEqual(runcLower, runcUpper) {
fmt.Printf("SAME: %v <--> %v\n", runcLower.Capabilities, runcUpper.Capabilities)
}
if reflect.DeepEqual(ociLower, ociUpper) {
fmt.Printf("SAME: %v <--> %v\n", ociLower.Capabilities, runcUpper.Capabilities)
}
}
utils_linux.go
Outdated
if err != nil { | ||
newuidmap = "" | ||
if newuidmap, err := exec.LookPath("newuidmap"); err == nil { | ||
f.NewuidmapPath = newuidmap |
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.
Almost tempted to say; perhaps it should be named LibContainer
(as it's constructed using libcontainer.New()
🤔
Even further; I'm even starting to wonder if libcontainer.New()
should be public at all. All cases except for getContainers()
("list containers"), basically follow the pattern;
factory := libcontainer.New()
factory.Create()
/factory.Load()
- done
It would almost make sense to just libcontainer.Create()
, libcontainer.Get()
, libcontainer.List()
(which internally can do a new()
), adding root
as argument.
type check func(config *configs.Config) error | ||
|
||
func (v *ConfigValidator) Validate(config *configs.Config) error { | ||
func Validate(config *configs.Config) 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.
Thanks for doing this! I recall looking at this a couple of times (why the "Validator"??)
Perhaps as a follow-up, we should move configs/validate
to configs
. It's a bit weird to have it in a separate package, as it's validating the config. Moving it into configs, if could just be validate(config)
(no need to export), or
func (config *configs.Config) validate() 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.
Perhaps as a follow-up, we should move
configs/validate
toconfigs
.
I gave it a try and got circular deps as a result. Will take a look later, but it seems this was the reason why it's in a separate package.
Oh, and we still have to export it because it's also used from libcontainer/specconv/spec_linux_test.go
// We resolve the paths for new{u,g}idmap from | ||
// the context of runc to avoid doing a path | ||
// lookup in the nsexec context. | ||
if path, err := exec.LookPath("newuidmap"); err == nil { |
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.
Silly question; I haven't fully checked which codepaths are executed, but I see that bootstrapData()
is called multiple times; can this lead to exec.LookPath()
being called multiple times (which will be iterating over all directories in PATH
); is that a concern? https://github.com/golang/go/blob/f229e7031a6efb2f23241b5da000c3b3203081d6/src/os/exec/lp_unix.go#L47-L57
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.
bootstrapData is caller either for runc start
/run
, or for runc init
if err := os.MkdirAll(root, 0o700); err != nil { | ||
return nil, err | ||
} | ||
return &Factory{ | ||
Root: root, | ||
Root: absRoot, |
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.
Wondering why we made Root
public; after all, its expected to only be set once (when doing libcontainer.New()
), then add a .Root()
function.
Once it's private, we should also validate (if needed) root
here, so that we can remove redundant checks such as
https://github.com/opencontainers/runc/blob/master/libcontainer/factory_linux.go#L145-L147
and
https://github.com/opencontainers/runc/blob/master/libcontainer/factory_linux.go#L220-L222
And remove redundant code, such as
Lines 118 to 122 in 6d35069
root := context.GlobalString("root") | |
absRoot, err := filepath.Abs(root) | |
if err != nil { | |
return nil, err | |
} |
Also, not 100% sure about this utility; perhaps it's still needed though (but it's a bit of an odd one)
Lines 99 to 101 in c9b8b4f
// reviseRootDir convert the root to absolute path | |
func reviseRootDir(context *cli.Context) error { | |
root := context.GlobalString("root") |
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.
Yes, I was going to clean this up.
I think it would make sense to move it separate (for visibility) |
OK, I am going to split this one into parts (and will update description accordingly). |
As per description, this was split into smaller PRs, of which all but #3385 were merged, so this can be closed now. |
This refactors the code to remove some interfaces, methods and tests that are not needed, and in general make code more comprehensible.
Splitting this one into more digestible pieces