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

Introducing FeatureGates and global feature config #344

Closed
wants to merge 3 commits into from

Conversation

ipatrykx
Copy link
Contributor

Regards discussion: #321

This PR introduces FeatureGates mechanism similar to one available in Kubernetes, that could be used by users to disable/enable features provided by SR-IOV DP. Additionally added global config structure that could hold configuration of features introduced in the future.

@@ -33,6 +35,8 @@ func flagInit(cp *cliParams) {
"JSON device pool config file location")
flag.StringVar(&cp.resourcePrefix, "resource-prefix", "intel.com",
"resource name prefix used for K8s extended resource")
flag.StringVar(&cp.featureGates, "feature-gates", "",
"resource name prefix used for K8s extended resource")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Update cmd description?

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

@@ -57,6 +61,25 @@ func main() {
glog.Fatalf("Exiting.. one or more invalid configuration(s) given")
return
}

// Read global config
if err := config.Config.ReadConfig(cp.configFile); err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you add an example of what the configMap looks like when featureGates data entry is added?
It looks like in the ReadConfig function, it is re-using the same cli cmd configFile which points to the resourceList data entry in configMap, but the umarshalling inside ReadConfig is failing when there is a resourceList data entry.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added the example as a part of manager_test, just for now:

}

// ReadConfig loads config
func (cfg *config) ReadConfig(configFile string) error {
Copy link
Collaborator

Choose a reason for hiding this comment

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

There is a readConfig in manager.go file to parse the resourceList data in configMap.
I think perhaps we want to combine both resourceList and featureGates to the same readConfig function and make it a sub-element of resourceManager.

Copy link
Contributor Author

@ipatrykx ipatrykx Apr 26, 2021

Choose a reason for hiding this comment

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

What I intended, was to make this config as independent of the existing 'manager config' as possible as it is still yet to be decided if we would like to use one big global config, or for example separate the device-discovery-related config and feature-related config (which I think is optional by it's own nature).

Copy link
Member

@martinkennelly martinkennelly left a comment

Choose a reason for hiding this comment

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

@ipatrykx I don't see where each features default "maturity" is located. My understanding is the user can toggle this via CLI or CM but where is the default? Thanks! Never mind, its for future features!

@@ -21,6 +21,8 @@ import (
"syscall"

"github.com/golang/glog"
"github.com/k8snetworkplumbingwg/sriov-network-device-plugin/pkg/config"
Copy link
Member

Choose a reason for hiding this comment

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

Nit: Separate imports according to convention [1]

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

)

// Config is global config object
var Config *config
Copy link
Member

Choose a reason for hiding this comment

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

Not a fan of globals unless necessary because when I was reading the 'main' file I had to go searching up the code to see where it was defined and then I see your new package addition is named config and guessed it was there.

I much rather have main call your newConfig func and then having a GetConfig function (with a check to make sure non-nil config, return error if it is). At least then, folks can clearly see where config is defined and coming from. WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed that to use getters. Also, after thinking about this I've decided that NewConfig() and NewFeatureGate() will not return the pointer to the config/fg but the usage of getter will be forced, so it should be clearly visible that this object is instantiated inside the package.


fgMap := make(map[string]bool)

if err = json.Unmarshal(allCfg["featureGates"], &fgMap); 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.

shouldn't you check if "featureGates" key is in the map before indexing? Potential panic if it isn't available.

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

)

// FeatureGate is feature gate 'manager' to be used
var FeatureGate *featureGate
Copy link
Member

Choose a reason for hiding this comment

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

Again, not a fan of globals. Can we use a Getter?

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.


var defaultSriovDpFeatureGates = map[feature]featureSpec{
AlphaFeature: {Default: false, Maturity: Alpha},
BetaFeature: {Default: true, Maturity: Beta},
Copy link
Member

Choose a reason for hiding this comment

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

@ipatrykx Beta to me seems redundant. I know the difference between Beta and GA in k8 is a feature cannot be turned off by default in GA, where as it can be in Beta. Seems like needless complexity for this software to have Beta.

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 beta

)

// Feature name
type feature string
Copy link
Member

Choose a reason for hiding this comment

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

not a blocker, but not a fan of aliasing base type string like this. WDYT? I think it makes it harder to read the code. Same with maturity above.

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 thought back then that it will make code more readable, but after thinking about this, changed that to use just string.

return fmt.Errorf("Feature %s is not supported", f)
}
fg.enabled[f] = status
if status == true && fg.knownFeatures[f].Maturity == Deprecated {
Copy link
Member

Choose a reason for hiding this comment

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

redundant comparison to true. Also, check if key in knownFeatures before indexing map.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Did you mean that is thousl be just `if status &&'?

As for the key checking, it's done in isFeatureSupported() which is called earlier in the code.

// SetFromMap sets the enablement status of featuers accordig to a map
func (fg *featureGate) SetFromMap(valuesToSet map[string]bool) error {
for k, v := range valuesToSet {
err := fg.set(feature(k), v)
Copy link
Member

Choose a reason for hiding this comment

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

nit: you should wrap your if statement around the set:
if err := fg.set.... err != nil {

}

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

}

// SetFromString converts config string to map and sets the enablement status of the selected features
// copied form k8s and slightly changed - TBC?
Copy link
Member

Choose a reason for hiding this comment

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

form - > from

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

func (fg *featureGate) SetFromString(value string) error {
featureMap := make(map[string]bool)
for _, s := range strings.Split(value, ",") {
if len(s) == 0 {
Copy link
Member

Choose a reason for hiding this comment

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

@ipatrykx Did you think of using flag.Var? User would need to specify flag feature-gates repeatedly but we wouldn't have to do this extraction of features which is complex. Probably need to rename feature-gates to feature-gate

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I was thinking of that, but using flags we will have to add a flag for every feature introduced, so decided that it will be better to do this the same way the k8s handles that (as far as I remember). Would like to hear more input on this from other though.

@ipatrykx
Copy link
Contributor Author

ipatrykx commented Oct 14, 2021

Hi, @martinkennelly, @zshi-redhat, @adrianchiris is it possible we could move this thing further? Also tagging @Eoghan1232 who was interested.

@coveralls
Copy link
Collaborator

coveralls commented Oct 28, 2022

Pull Request Test Coverage Report for Build 3345559109

Warning: This coverage report may be inaccurate.

This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.

Details

  • 1 of 37 (2.7%) changed or added relevant lines in 2 files are covered.
  • 26 unchanged lines in 3 files lost coverage.
  • Overall coverage decreased (-1.0%) to 76.163%

Changes Missing Coverage Covered Lines Changed/Added Lines %
cmd/sriovdp/main.go 0 36 0.0%
Files with Coverage Reduction New Missed Lines %
pkg/netdevice/netResourcePool.go 4 87.14%
cmd/sriovdp/main.go 7 0.0%
pkg/netdevice/vdpa.go 15 53.97%
Totals Coverage Status
Change from base Build 2733786348: -1.0%
Covered Lines: 1572
Relevant Lines: 2064

💛 - Coveralls

@ipatrykx
Copy link
Contributor Author

Hi all!
This PR has been rebased onto the current master.

@SchSeba
Copy link
Collaborator

SchSeba commented Dec 21, 2023

Is this PR still relevant or we can close it?

@SchSeba
Copy link
Collaborator

SchSeba commented Aug 19, 2024

closing this one

@SchSeba SchSeba closed this Aug 19, 2024
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.

None yet

5 participants