From 9cd9e0773be4bea2e83363d8e7b9f54d9c882765 Mon Sep 17 00:00:00 2001 From: Hongkai Liu Date: Wed, 11 Dec 2024 16:16:08 -0500 Subject: [PATCH] OTA-1010: pull GetImplicitlyEnabledCapabilities from the cvo repo --- pkg/capability/capability.go | 135 +++++++++++++++++++++ pkg/capability/capability_test.go | 193 ++++++++++++++++++++++++++++++ pkg/manifest/manifest.go | 6 +- 3 files changed, 333 insertions(+), 1 deletion(-) create mode 100644 pkg/capability/capability.go create mode 100644 pkg/capability/capability_test.go diff --git a/pkg/capability/capability.go b/pkg/capability/capability.go new file mode 100644 index 0000000000..9b7db80b57 --- /dev/null +++ b/pkg/capability/capability.go @@ -0,0 +1,135 @@ +package capability + +import ( + "sort" + + configv1 "github.com/openshift/api/config/v1" +) + +type ClusterCapabilities struct { + KnownCapabilities map[configv1.ClusterVersionCapability]struct{} + EnabledCapabilities map[configv1.ClusterVersionCapability]struct{} + ImplicitlyEnabledCapabilities []configv1.ClusterVersionCapability +} + +// GetImplicitlyEnabledCapabilities filters out from a set of clusterCapabilities +// the ones that +// - are not in the list enabledCapabilities of clusterCapabilities +// - are not the keys of clusterCapabilities.EnabledCapabilities +// - are not in the list clusterCapabilities.ImplicitlyEnabledCapabilities +// The returned list are clusterCapabilities which must be implicitly enabled. +func GetImplicitlyEnabledCapabilities(enabledCapabilities []configv1.ClusterVersionCapability, + capabilities []configv1.ClusterVersionCapability, + clusterCapabilities ClusterCapabilities) []configv1.ClusterVersionCapability { + var caps []configv1.ClusterVersionCapability + for _, c := range capabilities { + if contains(enabledCapabilities, c) { + continue + } + if _, ok := clusterCapabilities.EnabledCapabilities[c]; !ok { + if !contains(clusterCapabilities.ImplicitlyEnabledCapabilities, c) { + caps = append(caps, c) + } + } + } + sort.Sort(capabilitiesSort(caps)) + return caps +} + +func contains(caps []configv1.ClusterVersionCapability, capability configv1.ClusterVersionCapability) bool { + for _, c := range caps { + if capability == c { + return true + } + } + return false +} + +type capabilitiesSort []configv1.ClusterVersionCapability + +func (caps capabilitiesSort) Len() int { return len(caps) } +func (caps capabilitiesSort) Swap(i, j int) { caps[i], caps[j] = caps[j], caps[i] } +func (caps capabilitiesSort) Less(i, j int) bool { return string(caps[i]) < string(caps[j]) } + +// SetCapabilities populates and returns cluster clusterCapabilities from ClusterVersion clusterCapabilities spec. This method also +// ensures that no previously enabled capability is now disabled and returns any such implicitly enabled clusterCapabilities. +func SetCapabilities(config *configv1.ClusterVersion, + existingEnabled, alwaysEnabled map[configv1.ClusterVersionCapability]struct{}) ClusterCapabilities { + + var capabilities ClusterCapabilities + capabilities.KnownCapabilities = setKnownCapabilities() + + capabilities.EnabledCapabilities, capabilities.ImplicitlyEnabledCapabilities = setEnabledCapabilities(config.Spec.Capabilities, + existingEnabled, alwaysEnabled) + + return capabilities +} + +// setKnownCapabilities populates a map keyed by capability from all known clusterCapabilities as defined in ClusterVersion. +func setKnownCapabilities() map[configv1.ClusterVersionCapability]struct{} { + known := make(map[configv1.ClusterVersionCapability]struct{}) + + for _, v := range configv1.ClusterVersionCapabilitySets { + for _, capability := range v { + if _, ok := known[capability]; ok { + continue + } + known[capability] = struct{}{} + } + } + return known +} + +const ( + DefaultCapabilitySet = configv1.ClusterVersionCapabilitySetCurrent +) + +// setEnabledCapabilities populates a map keyed by capability from all enabled clusterCapabilities as defined in ClusterVersion. +// DefaultCapabilitySet is used if a baseline capability set is not defined by ClusterVersion. A check is then made to +// ensure that no previously enabled capability is now disabled and if any such clusterCapabilities are found each is enabled, +// saved, and returned. +// The required clusterCapabilities are added to the implicitly enabled. +func setEnabledCapabilities(capabilitiesSpec *configv1.ClusterVersionCapabilitiesSpec, + priorEnabled, alwaysEnabled map[configv1.ClusterVersionCapability]struct{}) (map[configv1.ClusterVersionCapability]struct{}, + []configv1.ClusterVersionCapability) { + + capSet := DefaultCapabilitySet + + if capabilitiesSpec != nil && len(capabilitiesSpec.BaselineCapabilitySet) > 0 { + capSet = capabilitiesSpec.BaselineCapabilitySet + } + enabled := GetCapabilitiesAsMap(configv1.ClusterVersionCapabilitySets[capSet]) + + if capabilitiesSpec != nil { + for _, v := range capabilitiesSpec.AdditionalEnabledCapabilities { + if _, ok := enabled[v]; ok { + continue + } + enabled[v] = struct{}{} + } + } + var implicitlyEnabled []configv1.ClusterVersionCapability + for k := range priorEnabled { + if _, ok := enabled[k]; !ok { + implicitlyEnabled = append(implicitlyEnabled, k) + enabled[k] = struct{}{} + } + } + for k := range alwaysEnabled { + if _, ok := enabled[k]; !ok { + implicitlyEnabled = append(implicitlyEnabled, k) + enabled[k] = struct{}{} + } + } + sort.Sort(capabilitiesSort(implicitlyEnabled)) + return enabled, implicitlyEnabled +} + +// GetCapabilitiesAsMap returns the slice of clusterCapabilities as a map with default values. +func GetCapabilitiesAsMap(capabilities []configv1.ClusterVersionCapability) map[configv1.ClusterVersionCapability]struct{} { + caps := make(map[configv1.ClusterVersionCapability]struct{}, len(capabilities)) + for _, c := range capabilities { + caps[c] = struct{}{} + } + return caps +} diff --git a/pkg/capability/capability_test.go b/pkg/capability/capability_test.go new file mode 100644 index 0000000000..006cfa2f38 --- /dev/null +++ b/pkg/capability/capability_test.go @@ -0,0 +1,193 @@ +package capability + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + + configv1 "github.com/openshift/api/config/v1" +) + +func TestGetImplicitlyEnabledCapabilities(t *testing.T) { + tests := []struct { + name string + enabledCaps []configv1.ClusterVersionCapability + capabilities []configv1.ClusterVersionCapability + clusterCapabilities ClusterCapabilities + expected []configv1.ClusterVersionCapability + }{ + {name: "implicitly enable capability", + enabledCaps: []configv1.ClusterVersionCapability{"cap1", "cap3"}, + capabilities: []configv1.ClusterVersionCapability{"cap2"}, + clusterCapabilities: ClusterCapabilities{ + EnabledCapabilities: map[configv1.ClusterVersionCapability]struct{}{"cap1": {}}, + }, + expected: []configv1.ClusterVersionCapability{"cap2"}, + }, + {name: "no prior caps, implicitly enabled capability", + capabilities: []configv1.ClusterVersionCapability{"cap2"}, + expected: []configv1.ClusterVersionCapability{"cap2"}, + }, + {name: "multiple implicitly enable capability", + enabledCaps: []configv1.ClusterVersionCapability{"cap1", "cap2", "cap3"}, + capabilities: []configv1.ClusterVersionCapability{"cap4", "cap5", "cap6"}, + expected: []configv1.ClusterVersionCapability{"cap4", "cap5", "cap6"}, + }, + {name: "no implicitly enable capability", + enabledCaps: []configv1.ClusterVersionCapability{"cap1", "cap3"}, + capabilities: []configv1.ClusterVersionCapability{"cap1"}, + clusterCapabilities: ClusterCapabilities{ + EnabledCapabilities: map[configv1.ClusterVersionCapability]struct{}{"cap1": {}}, + }, + }, + {name: "prior cap, no updated caps, no implicitly enabled capability", + enabledCaps: []configv1.ClusterVersionCapability{"cap1"}, + }, + {name: "no implicitly enable capability, already enabled", + enabledCaps: []configv1.ClusterVersionCapability{"cap1", "cap2"}, + capabilities: []configv1.ClusterVersionCapability{"cap2"}, + clusterCapabilities: ClusterCapabilities{ + EnabledCapabilities: map[configv1.ClusterVersionCapability]struct{}{"cap1": {}, "cap2": {}}, + }, + }, + {name: "no implicitly enable capability, new cap but already enabled", + enabledCaps: []configv1.ClusterVersionCapability{"cap1"}, + capabilities: []configv1.ClusterVersionCapability{"cap2"}, + clusterCapabilities: ClusterCapabilities{ + EnabledCapabilities: map[configv1.ClusterVersionCapability]struct{}{"cap2": {}}, + }, + }, + {name: "no implicitly enable capability, already implcitly enabled", + enabledCaps: []configv1.ClusterVersionCapability{"cap1"}, + capabilities: []configv1.ClusterVersionCapability{"cap2"}, + clusterCapabilities: ClusterCapabilities{ + EnabledCapabilities: map[configv1.ClusterVersionCapability]struct{}{"cap2": {}}, + ImplicitlyEnabledCapabilities: []configv1.ClusterVersionCapability{"cap2"}, + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + caps := GetImplicitlyEnabledCapabilities(test.enabledCaps, test.capabilities, test.clusterCapabilities) + if diff := cmp.Diff(test.expected, caps); diff != "" { + t.Errorf("%s: Returned capacities differ from expected:\n%s", test.name, diff) + } + }) + } +} + +func TestSetCapabilities(t *testing.T) { + tests := []struct { + name string + config *configv1.ClusterVersion + expected ClusterCapabilities + }{ + {name: "capabilities nil", + config: &configv1.ClusterVersion{}, + expected: ClusterCapabilities{ + KnownCapabilities: GetCapabilitiesAsMap(configv1.KnownClusterVersionCapabilities), + EnabledCapabilities: GetCapabilitiesAsMap(configv1.ClusterVersionCapabilitySets[DefaultCapabilitySet]), + }, + }, + {name: "capabilities set not set", + config: &configv1.ClusterVersion{ + Spec: configv1.ClusterVersionSpec{ + Capabilities: &configv1.ClusterVersionCapabilitiesSpec{}, + }, + }, + expected: ClusterCapabilities{ + KnownCapabilities: GetCapabilitiesAsMap(configv1.KnownClusterVersionCapabilities), + EnabledCapabilities: GetCapabilitiesAsMap(configv1.ClusterVersionCapabilitySets[DefaultCapabilitySet]), + }, + }, + {name: "set capabilities None", + config: &configv1.ClusterVersion{ + Spec: configv1.ClusterVersionSpec{ + Capabilities: &configv1.ClusterVersionCapabilitiesSpec{ + BaselineCapabilitySet: configv1.ClusterVersionCapabilitySetNone, + }, + }, + }, + expected: ClusterCapabilities{ + KnownCapabilities: GetCapabilitiesAsMap(configv1.KnownClusterVersionCapabilities), + EnabledCapabilities: GetCapabilitiesAsMap([]configv1.ClusterVersionCapability{}), + }, + }, + {name: "set capabilities 4_11", + config: &configv1.ClusterVersion{ + Spec: configv1.ClusterVersionSpec{ + Capabilities: &configv1.ClusterVersionCapabilitiesSpec{ + BaselineCapabilitySet: configv1.ClusterVersionCapabilitySet4_11, + AdditionalEnabledCapabilities: []configv1.ClusterVersionCapability{}, + }, + }, + }, + expected: ClusterCapabilities{ + KnownCapabilities: GetCapabilitiesAsMap(configv1.KnownClusterVersionCapabilities), + EnabledCapabilities: GetCapabilitiesAsMap([]configv1.ClusterVersionCapability{ + configv1.ClusterVersionCapabilityBaremetal, + configv1.ClusterVersionCapabilityMarketplace, + configv1.ClusterVersionCapabilityOpenShiftSamples, + configv1.ClusterVersionCapabilityMachineAPI, + }), + }, + }, + {name: "set capabilities vCurrent", + config: &configv1.ClusterVersion{ + Spec: configv1.ClusterVersionSpec{ + Capabilities: &configv1.ClusterVersionCapabilitiesSpec{ + BaselineCapabilitySet: configv1.ClusterVersionCapabilitySetCurrent, + AdditionalEnabledCapabilities: []configv1.ClusterVersionCapability{}, + }, + }, + }, + expected: ClusterCapabilities{ + KnownCapabilities: GetCapabilitiesAsMap(configv1.KnownClusterVersionCapabilities), + EnabledCapabilities: GetCapabilitiesAsMap(configv1.ClusterVersionCapabilitySets[configv1.ClusterVersionCapabilitySetCurrent]), + }, + }, + {name: "set capabilities None with additional", + config: &configv1.ClusterVersion{ + Spec: configv1.ClusterVersionSpec{ + Capabilities: &configv1.ClusterVersionCapabilitiesSpec{ + BaselineCapabilitySet: configv1.ClusterVersionCapabilitySetNone, + AdditionalEnabledCapabilities: []configv1.ClusterVersionCapability{"cap1", "cap2", "cap3"}, + }, + }, + }, + expected: ClusterCapabilities{ + KnownCapabilities: GetCapabilitiesAsMap(configv1.KnownClusterVersionCapabilities), + EnabledCapabilities: GetCapabilitiesAsMap([]configv1.ClusterVersionCapability{"cap1", "cap2", "cap3"}), + }, + }, + {name: "set capabilities 4_11 with additional", + config: &configv1.ClusterVersion{ + Spec: configv1.ClusterVersionSpec{ + Capabilities: &configv1.ClusterVersionCapabilitiesSpec{ + BaselineCapabilitySet: configv1.ClusterVersionCapabilitySet4_11, + AdditionalEnabledCapabilities: []configv1.ClusterVersionCapability{"cap1", "cap2", "cap3"}, + }, + }, + }, + expected: ClusterCapabilities{ + KnownCapabilities: GetCapabilitiesAsMap(configv1.KnownClusterVersionCapabilities), + EnabledCapabilities: GetCapabilitiesAsMap([]configv1.ClusterVersionCapability{ + configv1.ClusterVersionCapabilityBaremetal, + configv1.ClusterVersionCapabilityMarketplace, + configv1.ClusterVersionCapabilityOpenShiftSamples, + configv1.ClusterVersionCapabilityMachineAPI, + "cap1", + "cap2", + "cap3"}), + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + caps := SetCapabilities(test.config, nil, nil) + if diff := cmp.Diff(test.expected, caps); diff != "" { + t.Errorf("%s: Returned capacities differ from expected:\n%s", test.name, diff) + } + }) + } +} diff --git a/pkg/manifest/manifest.go b/pkg/manifest/manifest.go index d4082ce86b..8683851cfe 100644 --- a/pkg/manifest/manifest.go +++ b/pkg/manifest/manifest.go @@ -105,10 +105,14 @@ func (m *Manifest) String() string { return m.id.String() } -func (m Manifest) SameResourceID(manifest Manifest) bool { +func (m *Manifest) SameResourceID(manifest Manifest) bool { return m.id.equal(manifest.id) } +func (m *Manifest) GetManifestResourceId() string { + return m.id.String() +} + // UnmarshalJSON implements the json.Unmarshaler interface for the Manifest // type. It unmarshals bytes of a single kubernetes object to Manifest. func (m *Manifest) UnmarshalJSON(in []byte) error {