Skip to content
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

tuning: revert values on delete #540

Merged
merged 9 commits into from
Dec 9, 2020

Conversation

ipatrykx
Copy link
Contributor

@ipatrykx ipatrykx commented Oct 9, 2020

This PR will introduce NIC's attributes restoration as described in Issue #493: Tuning Plugin: Revert Values on Delete.

Currently when NIC's attributes (MAC, promiscuous mode, MTU) are changed by Tuning plugin, they remain as set even when pod is deleted and NIC is moved back to host's networking namespace. With this PR, JSON-based backup file will be created in dataDir (defaults to: /var/lib/cni/tuning), and it will be used to restore the NIC's attributes to original ones on pod deletion.

Readme and tests were updated as well to reflect those changes.

Values changed by Tuning plugin should be changed only for pod, therefore should be reverted when NIC is being moved from pod back to host.

Fixes issue containernetworking#493

Signed-off-by: Patryk Strusiewicz-Surmacki <patrykx.strusiewicz-surmacki@intel.com>
@squeed
Copy link
Member

squeed commented Oct 14, 2020

Any reason not to restore sysctls as well?

Copy link
Contributor

@bboreham bboreham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tens of lines of cosmetic changes make a PR harder to review, so please take them out.
If you like, make a separate PR that is just those changes.

@@ -258,7 +405,7 @@ func cmdCheck(args *skel.CmdArgs) error {

// Parse previous result.
if tuningConf.RawPrevResult == nil {
return fmt.Errorf("Required prevResult missing")
return fmt.Errorf("required prevResult missing")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cosmetic

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe I have deleted all the cosmetic changes.

plugins/meta/tuning/tuning_test.go Outdated Show resolved Hide resolved
if err != nil {
return fmt.Errorf("failed to marshall data for %q: %v", ifName, err)
}
if err = ioutil.WriteFile(path.Join(backupDir, ifName+".json"), data, 0600); err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it a wise choice to cache something inside container? Or /var/lib/cni/ on host is a better place? which is just being used by IPAM plugin host-local.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Backup file is stored in host's filesystem (/var/lib/cni/tunig by default). I think that storing backup in the container wouldn't be wise as container is not exactly persistent and might be for example deleted outside of the K8s. Additionally I think that if NIC is host's resource, therefore NIC's config should be stored on host itself.

Signed-off-by: Patryk Strusiewicz-Surmacki <patrykx.strusiewicz-surmacki@intel.com>
@ipatrykx
Copy link
Contributor Author

Any reason not to restore sysctls as well?

@squeed I believe that sysctls are set per namespace so setting those in container does not affect the host's setup, therefore I think that it is not necessary to restore this settings in any way.

user@control-plane ~ $ kubectl exec -it tuning-pod -- cat /proc/sys/net/core/somaxconn
500
user@worker ~ $ cat /proc/sys/net/core/somaxconn
128

}
}

backupDir := path.Join(backupPath, containerID)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than creating a directory per container, just join container id + ifname - that's what we do in other plugins.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I forgot about tests. Fixed.

Signed-off-by: Patryk Strusiewicz-Surmacki <patrykx.strusiewicz-surmacki@intel.com>
Signed-off-by: Patryk Strusiewicz-Surmacki <patrykx.strusiewicz-surmacki@intel.com>
Signed-off-by: Patryk Strusiewicz-Surmacki <patrykx.strusiewicz-surmacki@intel.com>
Copy link
Contributor

@bboreham bboreham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some thoughts below

@@ -205,6 +332,11 @@ func cmdAdd(args *skel.CmdArgs) error {
}
}

isTuned := (tuningConf.Mac != "" || tuningConf.Mtu != 0 || tuningConf.Promisc)
if err = createBackup(args.IfName, args.ContainerID, tuningConf.DataDir, isTuned, tuningConf); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we just skip creating a backup if nothing was tuned?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I initially though that it would be better to create a file even if nothing's changed just to avoid using the file existence check as indicator of whenever something has to be restored. However changed that as it seems that it may be more clear in general.

@@ -60,6 +66,14 @@ type IPAMArgs struct {
Mtu *int `json:"mtu,omitempty"`
}

// configToRestore will contain interface attributes that should be restored on cmdDel
type configToRestore struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It strikes me that this struct could be a part of TuningConf, which would remove the need to repeat all fields.

(and maybe part of IPAMArgs ?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bboreham I've changed the code. Renamed configToRestore to ifConfig and made it a part of IPAMArgs. I chose IPAMArgs because Promisc field in that structure is a pointer to bool same as in configToRestore. In case of json, simple bool will be omitted if it's value will be false and omitempty were specified. And I believe that in case of future development or just to avoid ambiguity it would be better to save exact value of Promisc in backup file whether it is true or false.

Which leads me to another question I was wondering regarding the Promisc bool in TuningConf. In original code of cmdAdd there is

if tuningConf.Promisc != false {
	if err = changePromisc(args.IfName, true); err != nil {
		return err
	}
}

So if user will have a NIC with promiscuous mode enabled, and would like to switch promisc off using tuning (by setting promisc to false), then tuning plugin will not disable the promiscuous mode on that NIC as the code inside if statement will not be executed. Is this an expected behaviour, e.g. linked to problems with some particular NIC or driver? If not, then Promisc bool could be changed to pointer, alongside MAC and MTU variables, and therefore ifConfig could be used in both IPAMArgs and TuningConf. I think that this shouldn't be a part of this PR anyway, so this question is just out of curiosity.

@@ -46,6 +46,8 @@ The parameters, "mac", "mtu" and "promisc", changes the interface attributes as
}
```

All the interface attributes set by tuning plugin will be restored to original values on container's removal.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

except sysctls?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made the info more precise.

Signed-off-by: Patryk Strusiewicz-Surmacki <patrykx.strusiewicz-surmacki@intel.com>
Promisc *bool `json:"promisc,omitempty"`
Mtu *int `json:"mtu,omitempty"`
SysCtl *map[string]string `json:"sysctl"`
IfConfig *ifConfig `json:"ifConfig,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be changing the configuration format, which we shouldn't do. Do you just want to embed the IfConfig struct?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was changed because of @bboreham request: #540 (comment)
I have explained this under that comment. If you need any more explanation, don't hesitate to ask.

Should I revert this change?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The explanation helps, but it does not override the point that we can't just make a breaking change to the API because you think it's nicer.
I did say "maybe"; if it can't be done, so be it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@squeed @bboreham Just reverted the API changes.

return fmt.Errorf("failed to remove file %v: %v", filePath, err)
}

if err = delDirIfEmpty(backupPath); err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that we are using a single data directory, there's probably no point in removing the directory at all.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed delDirIfEmpty and it's invocation.

@@ -37,9 +40,12 @@ import (
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
)

const defaultDataDir = "/var/lib/cni/tuning"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know we use /var/lib/cni for lots of other plugins, but that was a mistake I'm trying to undo. Can you make the default directory /run/cni/tuning please?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By using /run, we don't have to worry about cleaning up stale containers on reboot.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done :)

Signed-off-by: Patryk Strusiewicz-Surmacki <patrykx.strusiewicz-surmacki@intel.com>

config := configToRestore{}
if err = json.Unmarshal([]byte(file), &config); err != nil {
return fmt.Errorf("failed to unmarshall file %q: %v", filePath, err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this fails, just log an error and return; we don't want to block deletion.

Copy link
Contributor Author

@ipatrykx ipatrykx Dec 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How should be an error logged?
Returned nil for now.


_, err = netlink.LinkByName(ifName)
if err != nil {
return fmt.Errorf("failed to get %q: %v", ifName, err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Log here and return; the interface is clearly deleted :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above - just returned nil for now.


if config.Mtu != 0 {
if err = changeMtu(ifName, config.Mtu); err != nil {
err = fmt.Errorf("failed to restore MAC address: %v", err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"failed to restore MTU"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed :)

return err
}

err = ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now that I think about it, do we want to block deletion at all if this fails? When would there be an error that could be recoverable on retry?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I think you're right. Changed this to return always nil at the end, same as the 'stub' function that was here previously.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this

tuningConf, err := parseConf(args.StdinData, args.Args)
	if err != nil {
		return err
	}

also be forced to return nil?

Signed-off-by: Patryk Strusiewicz-Surmacki <patrykx.strusiewicz-surmacki@intel.com>
@squeed
Copy link
Member

squeed commented Dec 9, 2020

looks good to me.

@squeed squeed merged commit e13bab9 into containernetworking:master Dec 9, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants