Skip to content

Commit 2dffbc8

Browse files
committed
WIP: Alternate approach without use of global constants for DualStack support
1 parent 6675978 commit 2dffbc8

File tree

6 files changed

+97
-50
lines changed

6 files changed

+97
-50
lines changed

pkg/controller/clusterconfig/clusterconfig_controller.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ import (
2727
)
2828

2929
// and Start it when the Manager is Started.
30-
func Add(mgr manager.Manager, status *statusmanager.StatusManager, c cnoclient.Client, _ featuregates.FeatureGate) error {
31-
return add(mgr, newReconciler(mgr, status, c))
30+
func Add(mgr manager.Manager, status *statusmanager.StatusManager, c cnoclient.Client, featureGates featuregates.FeatureGate) error {
31+
return add(mgr, newReconciler(mgr, status, c, featureGates))
3232
}
3333

3434
// newReconciler returns a new reconcile.Reconciler
35-
func newReconciler(mgr manager.Manager, status *statusmanager.StatusManager, c cnoclient.Client) reconcile.Reconciler {
36-
return &ReconcileClusterConfig{client: c, scheme: mgr.GetScheme(), status: status}
35+
func newReconciler(mgr manager.Manager, status *statusmanager.StatusManager, c cnoclient.Client, featureGates featuregates.FeatureGate) reconcile.Reconciler {
36+
return &ReconcileClusterConfig{client: c, scheme: mgr.GetScheme(), status: status, featureGates: featureGates}
3737
}
3838

3939
// add adds a new Controller to mgr with r as the reconcile.Reconciler
@@ -57,9 +57,10 @@ var _ reconcile.Reconciler = &ReconcileClusterConfig{}
5757

5858
// ReconcileClusterConfig reconciles a cluster Network object
5959
type ReconcileClusterConfig struct {
60-
client cnoclient.Client
61-
scheme *runtime.Scheme
62-
status *statusmanager.StatusManager
60+
client cnoclient.Client
61+
scheme *runtime.Scheme
62+
status *statusmanager.StatusManager
63+
featureGates featuregates.FeatureGate
6364
}
6465

6566
// Reconcile propagates changes from the cluster config to the operator config.
@@ -92,7 +93,7 @@ func (r *ReconcileClusterConfig) Reconcile(ctx context.Context, request reconcil
9293
}
9394

9495
// Validate the cluster config
95-
if err := network.ValidateClusterConfig(clusterConfig, r.client); err != nil {
96+
if err := network.ValidateClusterConfig(clusterConfig, r.client, r.featureGates); err != nil {
9697
log.Printf("Failed to validate Network CR: %v", err)
9798
r.status.SetDegraded(statusmanager.ClusterConfig, "InvalidClusterConfig",
9899
fmt.Sprintf("The cluster configuration is invalid (%v). Use 'oc edit network.config.openshift.io cluster' to fix.", err))

pkg/controller/operconfig/cluster.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func (r *ReconcileOperConfig) MergeClusterConfig(ctx context.Context, operConfig
3131
}
3232
// Validate cluster config
3333
// If invalid just warn and proceed.
34-
if err := network.ValidateClusterConfig(clusterConfig, r.client); err != nil {
34+
if err := network.ValidateClusterConfig(clusterConfig, r.client, r.featureGates); err != nil {
3535
log.Printf("WARNING: ignoring Network.config.openshift.io/v1/cluster - failed validation: %v", err)
3636
return nil
3737
}

pkg/network/cluster_config.go

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"fmt"
66
"net"
7-
"strings"
87
"sync"
98

109
configv1 "github.com/openshift/api/config/v1"
@@ -15,6 +14,7 @@ import (
1514
"github.com/openshift/cluster-network-operator/pkg/names"
1615
"github.com/openshift/cluster-network-operator/pkg/platform"
1716
iputil "github.com/openshift/cluster-network-operator/pkg/util/ip"
17+
"github.com/openshift/library-go/pkg/operator/configobserver/featuregates"
1818
"k8s.io/apimachinery/pkg/api/meta"
1919
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2020
"k8s.io/apimachinery/pkg/types"
@@ -32,16 +32,16 @@ import (
3232
var pluginsUsingHostPrefix = sets.NewString(string(operv1.NetworkTypeOpenShiftSDN), string(operv1.NetworkTypeOVNKubernetes))
3333

3434
// ValidateClusterConfig ensures the cluster config is valid.
35-
func ValidateClusterConfig(clusterConfig *configv1.Network, client cnoclient.Client) error {
35+
func ValidateClusterConfig(clusterConfig *configv1.Network, client cnoclient.Client, featureGates featuregates.FeatureGate) error {
3636
// If for whatever reason it is not possible to get the platform type, fail
3737
infraRes, err := platform.InfraStatus(client)
3838
if err != nil {
3939
return err
4040
}
41-
return validateClusterConfig(clusterConfig, infraRes, client)
41+
return validateClusterConfig(clusterConfig, infraRes, client, featureGates)
4242
}
4343

44-
func validateClusterConfig(clusterConfig *configv1.Network, infraRes *bootstrap.InfraStatus, client cnoclient.Client) error {
44+
func validateClusterConfig(clusterConfig *configv1.Network, infraRes *bootstrap.InfraStatus, client cnoclient.Client, featureGates featuregates.FeatureGate) error {
4545

4646
// Check all networks for overlaps
4747
pool := iputil.IPPool{}
@@ -117,12 +117,9 @@ func validateClusterConfig(clusterConfig *configv1.Network, infraRes *bootstrap.
117117
return errors.Errorf("spec.networkType is required")
118118
}
119119

120-
// Validate that this is either a BareMetal or None PlatformType. For all other
121-
// PlatformTypes, migration to DualStack is prohibited
122120
if ipv4Service && ipv6Service || ipv4Cluster && ipv6Cluster {
123-
if !isSupportedDualStackPlatform(infraRes.PlatformType) {
124-
return errors.Errorf("%s is not one of the supported platforms for dual stack (%s)", infraRes.PlatformType,
125-
strings.Join(dualStackPlatforms.List(), ", "))
121+
if !isSupportedDualStackPlatform(infraRes.PlatformType, featureGates) {
122+
return errors.Errorf("%s is not one of the supported platforms for dual stack", infraRes.PlatformType)
126123
}
127124
}
128125

pkg/network/cluster_config_test.go

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ package network
22

33
import (
44
"fmt"
5-
"strings"
65
"testing"
76

87
configv1 "github.com/openshift/api/config/v1"
8+
apifeatures "github.com/openshift/api/features"
99
v1 "github.com/openshift/api/network/v1"
1010
operv1 "github.com/openshift/api/operator/v1"
1111
corev1 "k8s.io/api/core/v1"
@@ -17,6 +17,7 @@ import (
1717
"github.com/openshift/cluster-network-operator/pkg/client/fake"
1818
"github.com/openshift/cluster-network-operator/pkg/hypershift"
1919
"github.com/openshift/cluster-network-operator/pkg/names"
20+
"github.com/openshift/library-go/pkg/operator/configobserver/featuregates"
2021

2122
. "github.com/onsi/gomega"
2223
)
@@ -37,6 +38,20 @@ var ClusterConfig = configv1.NetworkSpec{
3738
NetworkType: "None",
3839
}
3940

41+
func getFeatureGatesWithDualStack() featuregates.FeatureGate {
42+
return featuregates.NewFeatureGate(
43+
[]configv1.FeatureGateName{apifeatures.FeatureGateAdminNetworkPolicy,
44+
apifeatures.FeatureGateDNSNameResolver,
45+
apifeatures.FeatureGateNetworkSegmentation,
46+
apifeatures.FeatureGateOVNObservability,
47+
apifeatures.FeatureGateAWSDualStackInstall,
48+
apifeatures.FeatureGateAzureDualStackInstall},
49+
[]configv1.FeatureGateName{
50+
apifeatures.FeatureGatePreconfiguredUDNAddresses,
51+
},
52+
)
53+
}
54+
4055
func TestValidateClusterConfig(t *testing.T) {
4156
g := NewGomegaWithT(t)
4257

@@ -57,12 +72,13 @@ func TestValidateClusterConfig(t *testing.T) {
5772
g.Expect(err).NotTo(HaveOccurred())
5873

5974
cc := *ClusterConfig.DeepCopy()
60-
err = ValidateClusterConfig(&configv1.Network{Spec: cc}, client)
75+
featureGates := getFeatureGatesWithDualStack()
76+
err = ValidateClusterConfig(&configv1.Network{Spec: cc}, client, featureGates)
6177
g.Expect(err).NotTo(HaveOccurred())
6278

6379
haveError := func(cfg configv1.NetworkSpec, substr string) {
6480
t.Helper()
65-
err = ValidateClusterConfig(&configv1.Network{Spec: cfg}, client)
81+
err = ValidateClusterConfig(&configv1.Network{Spec: cfg}, client, featureGates)
6682
g.Expect(err).To(MatchError(ContainSubstring(substr)))
6783
}
6884

@@ -88,7 +104,7 @@ func TestValidateClusterConfig(t *testing.T) {
88104

89105
cc = *ClusterConfig.DeepCopy()
90106
cc.ClusterNetwork[1].HostPrefix = 0
91-
res := ValidateClusterConfig(&configv1.Network{Spec: cc}, client)
107+
res := ValidateClusterConfig(&configv1.Network{Spec: cc}, client, featureGates)
92108
// Since the NetworkType is None, and the hostprefix is unset we don't validate it
93109
g.Expect(res).Should(BeNil())
94110

@@ -289,7 +305,8 @@ func TestValidClusterConfigLiveMigration(t *testing.T) {
289305
for _, tt := range tests {
290306
t.Run(tt.name, func(t *testing.T) {
291307
client := fake.NewFakeClient(tt.objects...)
292-
err := validateClusterConfig(tt.config, tt.infraRes, client)
308+
fg := getFeatureGatesWithDualStack()
309+
err := validateClusterConfig(tt.config, tt.infraRes, client, fg)
293310
if tt.expectErr {
294311
g.Expect(err).To(HaveOccurred())
295312
if tt.expectedErrorMsg != "" {
@@ -323,12 +340,13 @@ func TestValidateClusterConfigDualStack(t *testing.T) {
323340
g.Expect(err).NotTo(HaveOccurred())
324341

325342
cc := *ClusterConfig.DeepCopy()
326-
err = ValidateClusterConfig(&configv1.Network{Spec: cc}, client)
343+
featureGates := getFeatureGatesWithDualStack()
344+
err = ValidateClusterConfig(&configv1.Network{Spec: cc}, client, featureGates)
327345
g.Expect(err).NotTo(HaveOccurred())
328346

329347
haveError := func(cfg configv1.NetworkSpec, substr string) {
330348
t.Helper()
331-
err = ValidateClusterConfig(&configv1.Network{Spec: cc}, client)
349+
err = ValidateClusterConfig(&configv1.Network{Spec: cc}, client, featureGates)
332350
g.Expect(err).To(MatchError(ContainSubstring(substr)))
333351
}
334352

@@ -368,11 +386,14 @@ func TestValidateClusterConfigDualStack(t *testing.T) {
368386
CIDR: "fd01::/48",
369387
HostPrefix: 64,
370388
})
371-
err = ValidateClusterConfig(&configv1.Network{Spec: cc}, client)
389+
err = ValidateClusterConfig(&configv1.Network{Spec: cc}, client, featureGates)
372390
g.Expect(err).NotTo(HaveOccurred())
373391

374-
// You can't use dual-stack if this is anything else but BareMetal or NonePlatformType
375-
infrastructure.Status.PlatformStatus.Type = configv1.AzurePlatformType
392+
// You can't use dual-stack if enabled on an unsupported platform
393+
infrastructure.Status.PlatformStatus.Type = configv1.GCPPlatformType
394+
infrastructure.Status.PlatformStatus.GCP = &configv1.GCPPlatformStatus{
395+
Region: "us-west1",
396+
}
376397
client = fake.NewFakeClient(infrastructure)
377398
err = createProxy(client)
378399
g.Expect(err).NotTo(HaveOccurred())
@@ -382,8 +403,8 @@ func TestValidateClusterConfigDualStack(t *testing.T) {
382403
CIDR: "fd01::/48",
383404
HostPrefix: 64,
384405
})
385-
haveError(cc, fmt.Sprintf("%s is not one of the supported platforms for dual stack (%s)",
386-
infrastructure.Status.PlatformStatus.Type, strings.Join(dualStackPlatforms.List(), ", ")))
406+
haveError(cc, fmt.Sprintf("%s is not one of the supported platforms for dual stack",
407+
infrastructure.Status.PlatformStatus.Type))
387408
}
388409

389410
func TestMergeClusterConfig(t *testing.T) {

pkg/network/render.go

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,6 @@ import (
3131
"github.com/openshift/library-go/pkg/operator/configobserver/featuregates"
3232
)
3333

34-
var dualStackPlatforms = sets.NewString(
35-
string(configv1.BareMetalPlatformType),
36-
string(configv1.NonePlatformType),
37-
string(configv1.VSpherePlatformType),
38-
string(configv1.OpenStackPlatformType),
39-
)
40-
4134
const (
4235
pluginName = "networking-console-plugin"
4336
)
@@ -403,10 +396,7 @@ func isNetworkChangeSafe(prev, next *operv1.NetworkSpec, infraRes *bootstrap.Inf
403396
// Validate that this is either a BareMetal or None PlatformType. For all other
404397
// PlatformTypes, migration to DualStack is prohibited
405398
if len(prev.ServiceNetwork) < len(next.ServiceNetwork) {
406-
if !isSupportedDualStackPlatform(infraRes.PlatformType) {
407-
return errors.Errorf("%s is not one of the supported platforms for dual stack (%s)", infraRes.PlatformType,
408-
strings.Join(dualStackPlatforms.List(), ", "))
409-
} else if string(configv1.OpenStackPlatformType) == string(infraRes.PlatformType) {
399+
if !isConversionToDualStackSupported(infraRes.PlatformType) {
410400
return errors.Errorf("%s does not allow conversion to dual-stack cluster", infraRes.PlatformType)
411401
}
412402
}
@@ -973,10 +963,6 @@ func registerNetworkingConsolePlugin(bootstrapResult *bootstrap.BootstrapResult,
973963
})
974964
}
975965

976-
func isSupportedDualStackPlatform(platformType configv1.PlatformType) bool {
977-
return dualStackPlatforms.Has(string(platformType))
978-
}
979-
980966
func renderAdditionalRoutingCapabilities(conf *operv1.NetworkSpec, manifestDir string) ([]*uns.Unstructured, error) {
981967
if conf == nil || conf.AdditionalRoutingCapabilities == nil {
982968
return nil, nil
@@ -999,3 +985,25 @@ func renderAdditionalRoutingCapabilities(conf *operv1.NetworkSpec, manifestDir s
999985

1000986
return out, nil
1001987
}
988+
989+
func isSupportedDualStackPlatform(platformType configv1.PlatformType, featureGates featuregates.FeatureGate) bool {
990+
switch platformType {
991+
case configv1.BareMetalPlatformType, configv1.NonePlatformType, configv1.VSpherePlatformType, configv1.OpenStackPlatformType:
992+
return true
993+
case configv1.AWSPlatformType:
994+
return featureGates.Enabled(apifeatures.FeatureGateAWSDualStackInstall)
995+
case configv1.AzurePlatformType:
996+
return featureGates.Enabled(apifeatures.FeatureGateAzureDualStackInstall)
997+
default:
998+
return false
999+
}
1000+
}
1001+
1002+
func isConversionToDualStackSupported(platformType configv1.PlatformType) bool {
1003+
switch platformType {
1004+
case configv1.BareMetalPlatformType, configv1.NonePlatformType, configv1.VSpherePlatformType:
1005+
return true
1006+
default:
1007+
return false
1008+
}
1009+
}

pkg/network/render_test.go

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,18 @@ package network
33
import (
44
"fmt"
55
"reflect"
6-
"strings"
76

87
. "github.com/onsi/gomega"
98
"github.com/openshift/cluster-network-operator/pkg/client/fake"
109
"github.com/openshift/cluster-network-operator/pkg/hypershift"
10+
"github.com/openshift/library-go/pkg/operator/configobserver/featuregates"
1111
"github.com/stretchr/testify/assert"
1212
"k8s.io/client-go/kubernetes/scheme"
1313

1414
"testing"
1515

1616
configv1 "github.com/openshift/api/config/v1"
17+
apifeatures "github.com/openshift/api/features"
1718
operv1 "github.com/openshift/api/operator/v1"
1819
"github.com/openshift/cluster-network-operator/pkg/bootstrap"
1920
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -233,7 +234,7 @@ func TestDisallowMultipleClusterNetworksOfOldIPFamily(t *testing.T) {
233234
g.Expect(err).To(MatchError(ContainSubstring("cannot add additional ClusterNetwork values of original IP family when migrating to dual stack")))
234235
}
235236

236-
func TestAllowMigrationOnlyForBareMetalOrNoneType(t *testing.T) {
237+
func TestAllowMigrationOnlyForSupportedTypes(t *testing.T) {
237238
g, infra, prev, next := setupTestInfraAndBasicRenderConfigs(t, OVNKubernetesConfig, OVNKubernetesConfig)
238239

239240
// You can't migrate from single-stack to dual-stack if this is anything else but
@@ -246,9 +247,12 @@ func TestAllowMigrationOnlyForBareMetalOrNoneType(t *testing.T) {
246247
HostPrefix: 64,
247248
},
248249
)
250+
// You can't migrate from single-stack to dual-stack if this is anything else but
251+
// BareMetal, NonePlatformType, and VSphere
252+
infra.PlatformType = configv1.GCPPlatformType
253+
249254
err := IsChangeSafe(prev, next, infra)
250-
g.Expect(err).To(MatchError(ContainSubstring(fmt.Sprintf("%s is not one of the supported platforms for dual stack (%s)", infra.PlatformType,
251-
strings.Join(dualStackPlatforms.List(), ", ")))))
255+
g.Expect(err).To(MatchError(ContainSubstring(fmt.Sprintf("%s does not allow conversion to dual-stack cluster", infra.PlatformType))))
252256
// ... but the migration in the other direction should work
253257
err = IsChangeSafe(next, prev, infra)
254258
g.Expect(err).NotTo(HaveOccurred())
@@ -349,6 +353,20 @@ func TestDisallowCIDRMaskChangeInDualStackUpdate(t *testing.T) {
349353
g.Expect(err).To(MatchError(ContainSubstring("cannot add additional ClusterNetwork values of original IP family when migrating to dual stack")))
350354
}
351355

356+
func getDefaultFeatureGatesWithDualStack() featuregates.FeatureGate {
357+
return featuregates.NewFeatureGate(
358+
[]configv1.FeatureGateName{apifeatures.FeatureGateAdminNetworkPolicy,
359+
apifeatures.FeatureGateDNSNameResolver,
360+
apifeatures.FeatureGateNetworkSegmentation,
361+
apifeatures.FeatureGateOVNObservability,
362+
apifeatures.FeatureGateAWSDualStackInstall,
363+
apifeatures.FeatureGateAzureDualStackInstall},
364+
[]configv1.FeatureGateName{
365+
apifeatures.FeatureGatePreconfiguredUDNAddresses,
366+
},
367+
)
368+
}
369+
352370
func TestRenderUnknownNetwork(t *testing.T) {
353371
g := NewGomegaWithT(t)
354372

@@ -400,7 +418,9 @@ func TestRenderUnknownNetwork(t *testing.T) {
400418
bootstrapResult, err := Bootstrap(&config, client)
401419
g.Expect(err).NotTo(HaveOccurred())
402420

403-
objs, _, err := Render(prev, &configv1.NetworkSpec{}, manifestDir, client, getDefaultFeatureGates(), bootstrapResult)
421+
featureGatesCNO := getDefaultFeatureGatesWithDualStack()
422+
423+
objs, _, err := Render(prev, &configv1.NetworkSpec{}, manifestDir, client, featureGatesCNO, bootstrapResult)
404424
g.Expect(err).NotTo(HaveOccurred())
405425

406426
// Validate that openshift-sdn isn't rendered

0 commit comments

Comments
 (0)