forked from red-hat-storage/odf-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Umanga Chapagain <[email protected]>
- Loading branch information
1 parent
c910ed5
commit cc59bcf
Showing
9 changed files
with
279 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package get | ||
|
||
import ( | ||
"github.com/red-hat-storage/odf-cli/pkg/odf/dr" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
var drPrereqCmd = &cobra.Command{ | ||
Use: "dr-prereq", | ||
Short: "Print the status of pre-requisites for Disaster Recovery between peer clusters.", | ||
DisableFlagParsing: true, | ||
Args: cobra.ExactArgs(1), | ||
Example: "odf get dr-prereq <PeerManagedClusterName>", | ||
Run: func(cmd *cobra.Command, args []string) { | ||
ctx := cmd.Context() | ||
dr.GetDRPrerequisite(ctx, args[0]) | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package dr | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"reflect" | ||
|
||
"github.com/red-hat-storage/odf-cli/cmd/odf/root" | ||
"github.com/rook/kubectl-rook-ceph/pkg/logging" | ||
submarinerv1alpha1 "github.com/submariner-io/submariner-operator/api/v1alpha1" | ||
submarinerv1 "github.com/submariner-io/submariner/pkg/apis/submariner.io/v1" | ||
"github.com/submariner-io/submariner/pkg/cidr" | ||
"k8s.io/apimachinery/pkg/api/errors" | ||
"k8s.io/apimachinery/pkg/api/meta" | ||
"k8s.io/apimachinery/pkg/types" | ||
ctrl "sigs.k8s.io/controller-runtime/pkg/client" | ||
) | ||
|
||
const submarinerOperatorNamespace string = "submariner-operator" | ||
|
||
func GetDRPrerequisite(ctx context.Context, peerClusterID string) { | ||
submarinerCR, err := isSubmarinerEnabled(ctx, root.CtrlClient) | ||
if err != nil { | ||
logging.Fatal(err) | ||
} | ||
|
||
if reflect.DeepEqual(submarinerCR, submarinerv1alpha1.Submariner{}) { | ||
logging.Info("Submariner is not installed.") | ||
} else { | ||
logging.Info("Submariner is installed.") | ||
} | ||
|
||
localClusterID := submarinerCR.Status.ClusterID | ||
globalnetRequired, err := isGlobalnetRequired(ctx, root.CtrlClient, localClusterID, peerClusterID) | ||
if err != nil { | ||
logging.Fatal(err) | ||
} | ||
if globalnetRequired { | ||
logging.Info("Globalnet is required.") | ||
} else { | ||
logging.Info("Globalnet is not required.") | ||
} | ||
|
||
globalnetEnabled := isGlobalnetEnabled(submarinerCR) | ||
if globalnetEnabled { | ||
logging.Info("Globalnet is enabled.") | ||
} else { | ||
logging.Info("Globalnet is not enabled.") | ||
} | ||
} | ||
|
||
func isSubmarinerEnabled(ctx context.Context, client ctrl.Client) (submarinerv1alpha1.Submariner, error) { | ||
submarinerCR := submarinerv1alpha1.Submariner{} | ||
err := client.Get(ctx, types.NamespacedName{Name: "submariner", Namespace: submarinerOperatorNamespace}, &submarinerCR) | ||
if err != nil { | ||
// These errors mean that Submariner is not installed. | ||
// IsNoMatchError -> Submariner CRD is not available. | ||
// IsNotFound -> Submariner CR is not created. | ||
if meta.IsNoMatchError(err) || errors.IsNotFound(err) { | ||
return submarinerCR, nil | ||
} | ||
return submarinerCR, err | ||
} | ||
return submarinerCR, nil | ||
} | ||
|
||
func isGlobalnetRequired(ctx context.Context, client ctrl.Client, clusterID, peerClusterID string) (bool, error) { | ||
if clusterID == peerClusterID { | ||
return false, fmt.Errorf("Current ClusterID and peer ClusterID refer to the same cluster. Provide a different peer ClusterID.") | ||
} | ||
|
||
clusterCR := &submarinerv1.Cluster{} | ||
err := client.Get(ctx, types.NamespacedName{Name: clusterID, Namespace: submarinerOperatorNamespace}, clusterCR) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
peerClusterCR := &submarinerv1.Cluster{} | ||
err = client.Get(ctx, types.NamespacedName{Name: peerClusterID, Namespace: submarinerOperatorNamespace}, peerClusterCR) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
err = cidr.OverlappingSubnets(clusterCR.Spec.ServiceCIDR, clusterCR.Spec.ClusterCIDR, | ||
append(peerClusterCR.Spec.ClusterCIDR, peerClusterCR.Spec.ServiceCIDR...)) | ||
if err != nil { | ||
return true, nil | ||
} | ||
|
||
return false, nil | ||
} | ||
|
||
func isGlobalnetEnabled(submarinerCR submarinerv1alpha1.Submariner) bool { | ||
if submarinerCR.Status.GlobalnetDaemonSetStatus.Status == nil { | ||
return false | ||
} | ||
return submarinerCR.Status.GlobalnetDaemonSetStatus.Status.NumberAvailable > 0 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package dr | ||
|
||
import ( | ||
"reflect" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
submarinerv1alpha1 "github.com/submariner-io/submariner-operator/api/v1alpha1" | ||
submarinerv1 "github.com/submariner-io/submariner/pkg/apis/submariner.io/v1" | ||
"golang.org/x/net/context" | ||
appsv1 "k8s.io/api/apps/v1" | ||
"k8s.io/apimachinery/pkg/api/errors" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" | ||
ctrl "sigs.k8s.io/controller-runtime/pkg/client/fake" | ||
"sigs.k8s.io/controller-runtime/pkg/client/interceptor" | ||
) | ||
|
||
func Test_isSubmarinerEnabled(t *testing.T) { | ||
var scheme = runtime.NewScheme() | ||
err := submarinerv1alpha1.AddToScheme(scheme) | ||
assert.NoError(t, err) | ||
|
||
ctx := context.TODO() | ||
|
||
client := ctrl.NewClientBuilder().WithScheme(scheme).Build() | ||
submarinerCR, err := isSubmarinerEnabled(ctx, client) | ||
assert.NoError(t, err) | ||
assert.True(t, reflect.DeepEqual(submarinerCR, submarinerv1alpha1.Submariner{})) | ||
|
||
err = client.Create(ctx, &submarinerv1alpha1.Submariner{ObjectMeta: metav1.ObjectMeta{Name: "submariner", Namespace: "submariner-operator"}}) | ||
assert.NoError(t, err) | ||
|
||
submarinerCR, err = isSubmarinerEnabled(ctx, client) | ||
assert.NoError(t, err) | ||
assert.False(t, reflect.DeepEqual(submarinerCR, submarinerv1alpha1.Submariner{})) | ||
} | ||
|
||
func Test_isGlobalnetRequired(t *testing.T) { | ||
var scheme = runtime.NewScheme() | ||
err := submarinerv1.AddToScheme(scheme) | ||
assert.NoError(t, err) | ||
|
||
ctx := context.TODO() | ||
|
||
client := ctrl.NewClientBuilder().WithScheme(scheme).Build() | ||
res, err := isGlobalnetRequired(ctx, client, "cluster1", "cluster1") | ||
assert.Error(t, err) | ||
assert.False(t, res) | ||
|
||
clusterCR := &submarinerv1.Cluster{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "cluster1", | ||
Namespace: submarinerOperatorNamespace, | ||
}, | ||
Spec: submarinerv1.ClusterSpec{ | ||
ServiceCIDR: []string{"172.30.0.0/16"}, | ||
ClusterCIDR: []string{"10.128.0.0/14"}, | ||
}, | ||
} | ||
peerClusterCR := &submarinerv1.Cluster{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "cluster2", | ||
Namespace: submarinerOperatorNamespace, | ||
}, | ||
Spec: submarinerv1.ClusterSpec{ | ||
ServiceCIDR: []string{"172.40.0.0/16"}, | ||
ClusterCIDR: []string{"10.138.0.0/14"}, | ||
}, | ||
} | ||
assert.NoError(t, client.Create(ctx, clusterCR)) | ||
assert.NoError(t, client.Create(ctx, peerClusterCR)) | ||
|
||
res, err = isGlobalnetRequired(ctx, client, "cluster1", "cluster2") | ||
assert.NoError(t, err) | ||
assert.False(t, res) | ||
|
||
peerClusterWithOverlappingIP := &submarinerv1.Cluster{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: "cluster3", | ||
Namespace: submarinerOperatorNamespace, | ||
}, | ||
Spec: submarinerv1.ClusterSpec{ | ||
ServiceCIDR: []string{"172.30.0.0/16"}, | ||
ClusterCIDR: []string{"10.128.0.0/14"}, | ||
}, | ||
} | ||
assert.NoError(t, client.Create(ctx, peerClusterWithOverlappingIP)) | ||
|
||
res, err = isGlobalnetRequired(ctx, client, "cluster1", "cluster3") | ||
assert.NoError(t, err) | ||
assert.True(t, res) | ||
|
||
res, err = isGlobalnetRequired(ctx, client, "cluster2", "cluster3") | ||
assert.NoError(t, err) | ||
assert.False(t, res) | ||
|
||
client = ctrl.NewClientBuilder().WithInterceptorFuncs(interceptor.Funcs{ | ||
Get: func(ctx context.Context, client ctrlclient.WithWatch, key ctrlclient.ObjectKey, obj ctrlclient.Object, opts ...ctrlclient.GetOption) error { | ||
return errors.NewNotFound(submarinerv1.Resource("clusters"), "cluster1") | ||
}, | ||
}).WithObjects(clusterCR, peerClusterCR, peerClusterWithOverlappingIP).WithScheme(scheme).Build() | ||
res, err = isGlobalnetRequired(ctx, client, "cluster1", "cluster3") | ||
assert.Error(t, err) | ||
assert.False(t, res) | ||
} | ||
|
||
func Test_isGlobalnetEnabled(t *testing.T) { | ||
submarinerCR := submarinerv1alpha1.Submariner{} | ||
assert.False(t, isGlobalnetEnabled(submarinerCR)) | ||
|
||
submarinerCR.Status.GlobalnetDaemonSetStatus = submarinerv1alpha1.DaemonSetStatusWrapper{ | ||
Status: &appsv1.DaemonSetStatus{ | ||
NumberAvailable: 0, | ||
}, | ||
} | ||
assert.False(t, isGlobalnetEnabled(submarinerCR)) | ||
|
||
submarinerCR.Status.GlobalnetDaemonSetStatus.Status.NumberAvailable = 1 | ||
assert.True(t, isGlobalnetEnabled(submarinerCR)) | ||
} |