Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions cmd/ocm-backplane/login/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ var (
remediation string
govcloud bool
readonly bool
readwrite bool
}

// loginType derive the login type based on flags and args
Expand Down Expand Up @@ -138,7 +139,14 @@ func init() {
&args.readonly,
"readonly",
false,
"Login with read-only access to the cluster",
"(Deprecated) Login with read-only access to the cluster (this is now the default behavior)",
)
_ = flags.MarkDeprecated("readonly", "read-only is now the default behavior, use --rw to login with write access")
flags.BoolVar(
&args.readwrite,
"rw",
false,
"Login with read-write access to the cluster",
)
}

Expand Down Expand Up @@ -340,13 +348,17 @@ func runLogin(cmd *cobra.Command, argv []string) (err error) {
return fmt.Errorf("cluster %s is hibernating, login failed", clusterKey)
}

// Determine if login should be readonly (default) or readwrite
// Default is readonly unless --rw is explicitly set
isReadOnly := !args.readwrite

logger.WithFields(logger.Fields{
"bpURL": bpURL,
"clusterID": clusterID,
"readonly": args.readonly,
"readonly": isReadOnly,
}).Debugln("Query backplane-api for proxy url of our target cluster")
// Query backplane-api for proxy url
bpAPIClusterURL, err := doLoginWithConn(bpURL, clusterID, *accessToken, nil, args.readonly)
bpAPIClusterURL, err := doLoginWithConn(bpURL, clusterID, *accessToken, nil, isReadOnly)
if err != nil {
// Declare helperMsg
helperMsg := "\n\033[1mNOTE: To troubleshoot the connectivity issues, please run `ocm-backplane health-check`\033[0m\n\n"
Expand Down
41 changes: 21 additions & 20 deletions cmd/ocm-backplane/login/login_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ var _ = Describe("Login command", func() {
globalOpts.Service = false
globalOpts.BackplaneURL = ""
globalOpts.ProxyURL = ""
args.readwrite = false
_ = os.Setenv("HTTPS_PROXY", "")
_ = os.Unsetenv("BACKPLANE_CONFIG")
_ = os.Remove(bpConfigPath)
Expand Down Expand Up @@ -150,7 +151,7 @@ var _ = Describe("Login command", func() {
mockOcmInterface.EXPECT().IsClusterHibernating(gomock.Eq(trueClusterID)).Return(false, nil).AnyTimes()
mockOcmInterface.EXPECT().GetOCMAccessToken().Return(&testToken, nil)
mockClientUtil.EXPECT().MakeRawBackplaneAPIClientWithAccessToken(backplaneAPIURI, testToken).Return(mockClient, nil)
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(trueClusterID)).Return(fakeResp, nil)
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(trueClusterID), gomock.Any()).Return(fakeResp, nil)

err = runLogin(nil, []string{testClusterID})

Expand All @@ -174,7 +175,7 @@ var _ = Describe("Login command", func() {
mockOcmInterface.EXPECT().IsClusterHibernating(gomock.Eq(trueClusterID)).Return(false, nil).AnyTimes()
mockOcmInterface.EXPECT().GetOCMAccessToken().Return(&testToken, nil)
mockClientUtil.EXPECT().MakeRawBackplaneAPIClientWithAccessToken("https://sadge.app", testToken).Return(mockClient, nil)
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(trueClusterID)).Return(fakeResp, nil)
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(trueClusterID), gomock.Any()).Return(fakeResp, nil)

err = runLogin(nil, []string{testClusterID})

Expand All @@ -199,7 +200,7 @@ var _ = Describe("Login command", func() {
mockOcmInterface.EXPECT().IsClusterHibernating(gomock.Eq(trueClusterID)).Return(false, nil).AnyTimes()
mockOcmInterface.EXPECT().GetOCMAccessToken().Return(&testToken, nil)
mockClientUtil.EXPECT().MakeRawBackplaneAPIClientWithAccessToken(backplaneAPIURI, testToken).Return(mockClient, nil)
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(trueClusterID)).Return(fakeResp, nil)
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(trueClusterID), gomock.Any()).Return(fakeResp, nil)

err = runLogin(nil, []string{testClusterID})

Expand Down Expand Up @@ -256,7 +257,7 @@ var _ = Describe("Login command", func() {
mockOcmInterface.EXPECT().IsClusterHibernating(gomock.Eq(trueClusterID)).Return(false, nil).AnyTimes()
mockOcmInterface.EXPECT().GetOCMAccessToken().Return(&testToken, nil)
mockClientUtil.EXPECT().MakeRawBackplaneAPIClientWithAccessToken(backplaneAPIURI, testToken).Return(mockClient, nil)
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(trueClusterID)).Return(fakeResp, nil)
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(trueClusterID), gomock.Any()).Return(fakeResp, nil)
mockOcmInterface.EXPECT().GetClusterInfoByID(gomock.Any()).Return(mockCluster, nil).Times(2)
mockOcmInterface.EXPECT().SetupOCMConnection().Return(nil, nil)
mockOcmInterface.EXPECT().IsClusterAccessProtectionEnabled(gomock.Any(), trueClusterID).Return(false, nil)
Expand All @@ -277,7 +278,7 @@ var _ = Describe("Login command", func() {
mockOcmInterface.EXPECT().IsClusterHibernating(gomock.Eq(trueClusterID)).Return(false, nil).AnyTimes()
mockOcmInterface.EXPECT().GetOCMAccessToken().Return(&testToken, nil).AnyTimes()
mockClientUtil.EXPECT().MakeRawBackplaneAPIClientWithAccessToken(backplaneAPIURI, testToken).Return(mockClient, nil).AnyTimes()
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(trueClusterID)).Return(fakeResp, nil).AnyTimes()
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(trueClusterID), gomock.Any()).Return(fakeResp, nil).AnyTimes()

// Mock PrintClusterInfo to return an error
mockOcmInterface.EXPECT().GetClusterInfoByID(gomock.Any()).Return(nil, errors.New("mock error"))
Expand All @@ -295,7 +296,7 @@ var _ = Describe("Login command", func() {
mockOcmInterface.EXPECT().IsClusterHibernating(gomock.Eq(trueClusterID)).Return(false, nil).AnyTimes()
mockOcmInterface.EXPECT().GetOCMAccessToken().Return(&testToken, nil)
mockClientUtil.EXPECT().MakeRawBackplaneAPIClientWithAccessToken(backplaneAPIURI, testToken).Return(mockClient, nil)
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(trueClusterID)).Return(fakeResp, nil)
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(trueClusterID), gomock.Any()).Return(fakeResp, nil)

err = runLogin(nil, []string{testClusterID})

Expand All @@ -318,7 +319,7 @@ var _ = Describe("Login command", func() {
mockOcmInterface.EXPECT().IsClusterHibernating(gomock.Eq(trueClusterID)).Return(false, nil).AnyTimes()
mockOcmInterface.EXPECT().GetOCMAccessToken().Return(&testToken, nil)
mockClientUtil.EXPECT().MakeRawBackplaneAPIClientWithAccessToken(backplaneAPIURI, testToken).Return(mockClient, nil)
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(trueClusterID)).Return(fakeResp, nil)
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(trueClusterID), gomock.Any()).Return(fakeResp, nil)

err = runLogin(nil, []string{testClusterID})

Expand Down Expand Up @@ -353,7 +354,7 @@ var _ = Describe("Login command", func() {
mockOcmInterface.EXPECT().IsClusterHibernating(gomock.Eq(managingClusterID)).Return(false, nil).AnyTimes()
mockOcmInterface.EXPECT().GetOCMAccessToken().Return(&testToken, nil)
mockClientUtil.EXPECT().MakeRawBackplaneAPIClientWithAccessToken(backplaneAPIURI, testToken).Return(mockClient, nil)
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(managingClusterID)).Return(fakeResp, nil)
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(managingClusterID), gomock.Any()).Return(fakeResp, nil)

err := runLogin(nil, []string{testClusterID})

Expand All @@ -373,7 +374,7 @@ var _ = Describe("Login command", func() {
mockOcmInterface.EXPECT().IsClusterHibernating(gomock.Eq(managingClusterID)).Return(false, nil).AnyTimes()
mockOcmInterface.EXPECT().GetOCMAccessToken().Return(&testToken, nil).AnyTimes()
mockClientUtil.EXPECT().MakeRawBackplaneAPIClientWithAccessToken(backplaneAPIURI, testToken).Return(mockClient, nil).AnyTimes()
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(managingClusterID)).Return(fakeResp, nil).AnyTimes()
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(managingClusterID), gomock.Any()).Return(fakeResp, nil).AnyTimes()

err := runLogin(nil, []string{testClusterID})

Expand All @@ -391,7 +392,7 @@ var _ = Describe("Login command", func() {
mockOcmInterface.EXPECT().IsClusterHibernating(gomock.Eq(serviceClusterID)).Return(false, nil).AnyTimes()
mockOcmInterface.EXPECT().GetOCMAccessToken().Return(&testToken, nil)
mockClientUtil.EXPECT().MakeRawBackplaneAPIClientWithAccessToken(backplaneAPIURI, testToken).Return(mockClient, nil)
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(serviceClusterID)).Return(fakeResp, nil)
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(serviceClusterID), gomock.Any()).Return(fakeResp, nil)

err := runLogin(nil, []string{testClusterID})

Expand All @@ -409,7 +410,7 @@ var _ = Describe("Login command", func() {
mockOcmInterface.EXPECT().IsClusterHibernating(gomock.Eq(testClusterID)).Return(false, nil).AnyTimes()
mockOcmInterface.EXPECT().GetOCMAccessToken().Return(&testToken, nil)
mockClientUtil.EXPECT().MakeRawBackplaneAPIClientWithAccessToken(backplaneAPIURI, testToken).Return(mockClient, nil)
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(testClusterID)).Return(fakeResp, nil)
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(testClusterID), gomock.Any()).Return(fakeResp, nil)

err = runLogin(nil, nil)

Expand All @@ -431,7 +432,7 @@ var _ = Describe("Login command", func() {
mockOcmInterface.EXPECT().IsClusterHibernating(gomock.Eq(trueClusterID)).Return(false, nil).AnyTimes()
mockOcmInterface.EXPECT().GetOCMAccessToken().Return(&testToken, nil)
mockClientUtil.EXPECT().MakeRawBackplaneAPIClientWithAccessToken(backplaneAPIURI, testToken).Return(mockClient, nil)
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(trueClusterID)).Return(fakeResp, errors.New("dial tcp: lookup yourproxy.com: no such host"))
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(trueClusterID), gomock.Any()).Return(fakeResp, errors.New("dial tcp: lookup yourproxy.com: no such host"))

err = runLogin(nil, []string{testClusterID})

Expand Down Expand Up @@ -459,7 +460,7 @@ var _ = Describe("Login command", func() {
mockOcmInterface.EXPECT().IsClusterHibernating(gomock.Eq(trueClusterID)).Return(false, nil).AnyTimes()
mockOcmInterface.EXPECT().GetOCMAccessToken().Return(&testToken, nil)
mockClientUtil.EXPECT().MakeRawBackplaneAPIClientWithAccessToken(backplaneAPIURI, testToken).Return(mockClient, nil)
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(trueClusterID)).Return(fakeResp, nil)
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(trueClusterID), gomock.Any()).Return(fakeResp, nil)

err = runLogin(nil, []string{testClusterID})

Expand Down Expand Up @@ -598,7 +599,7 @@ var _ = Describe("Login command", func() {
mockOcmInterface.EXPECT().GetClusterInfoByID(testClusterID).Return(mockCluster, nil)
mockOcmInterface.EXPECT().GetOCMAccessToken().Return(&testToken, nil)
mockClientUtil.EXPECT().MakeRawBackplaneAPIClientWithAccessToken(backplaneAPIURI, testToken).Return(mockClient, nil)
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(testClusterID)).Return(fakeResp, nil)
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(testClusterID), gomock.Any()).Return(fakeResp, nil)

username := "test-user"

Expand All @@ -613,7 +614,7 @@ var _ = Describe("Login command", func() {
mockOcmInterface.EXPECT().GetClusterInfoByID(testClusterID).Return(mockCluster, nil)
mockOcmInterface.EXPECT().GetOCMAccessToken().Return(&testToken, nil)
mockClientUtil.EXPECT().MakeRawBackplaneAPIClientWithAccessToken(backplaneAPIURI, testToken).Return(mockClient, nil)
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(testClusterID)).Return(fakeResp, nil)
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(testClusterID), gomock.Any()).Return(fakeResp, nil)

username := "test-user"
elevationReasons := []string{"reason1", "reason2"}
Expand Down Expand Up @@ -660,7 +661,7 @@ var _ = Describe("Login command", func() {
mockOcmInterface.EXPECT().IsClusterHibernating(gomock.Eq(testClusterID)).Return(false, nil).AnyTimes()
mockOcmInterface.EXPECT().GetOCMAccessToken().Return(&testToken, nil)
mockClientUtil.EXPECT().MakeRawBackplaneAPIClientWithAccessToken(backplaneAPIURI, testToken).Return(mockClient, nil)
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(testClusterID)).Return(fakeResp, nil)
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(testClusterID), gomock.Any()).Return(fakeResp, nil)

err = runLogin(nil, nil)

Expand Down Expand Up @@ -692,10 +693,10 @@ var _ = Describe("Login command", func() {
Expect(err).To(BeNil())
})

It("should add readonly=true query parameter when readonly flag is set", func() {
It("should add readonly=true query parameter by default (when --rw flag is not set)", func() {
// Setup
args.multiCluster = false
args.readonly = true
args.readwrite = false
loginType = LoginTypeClusterID

mockOcmInterface.EXPECT().GetOCMEnvironment().Return(ocmEnv, nil).AnyTimes()
Expand Down Expand Up @@ -728,10 +729,10 @@ var _ = Describe("Login command", func() {
Expect(capturedURL).To(ContainSubstring("readonly=true"))
})

It("should not add readonly query parameter when readonly flag is false", func() {
It("should not add readonly query parameter when --rw flag is set", func() {
// Setup
args.multiCluster = false
args.readonly = false
args.readwrite = true
loginType = LoginTypeClusterID

mockOcmInterface.EXPECT().GetOCMEnvironment().Return(ocmEnv, nil).AnyTimes()
Expand Down
2 changes: 2 additions & 0 deletions cmd/ocm-backplane/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/openshift/backplane-cli/cmd/ocm-backplane/mcp"
"github.com/openshift/backplane-cli/cmd/ocm-backplane/monitoring"
"github.com/openshift/backplane-cli/cmd/ocm-backplane/remediation"
"github.com/openshift/backplane-cli/cmd/ocm-backplane/rw"
"github.com/openshift/backplane-cli/cmd/ocm-backplane/script"
"github.com/openshift/backplane-cli/cmd/ocm-backplane/session"
"github.com/openshift/backplane-cli/cmd/ocm-backplane/status"
Expand Down Expand Up @@ -86,4 +87,5 @@ func init() {
rootCmd.AddCommand(monitoring.MonitoringCmd)
rootCmd.AddCommand(healthcheck.HealthCheckCmd)
rootCmd.AddCommand(remediation.NewRemediationCmd())
rootCmd.AddCommand(rw.RwCmd)
}
59 changes: 59 additions & 0 deletions cmd/ocm-backplane/rw/rw.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package rw

import (
"fmt"

logger "github.com/sirupsen/logrus"
"github.com/spf13/cobra"

"github.com/openshift/backplane-cli/cmd/ocm-backplane/login"
"github.com/openshift/backplane-cli/pkg/utils"
)

// RwCmd represents the rw command
var RwCmd = &cobra.Command{
Use: "rw",
Short: "Re-login to the current cluster with read-write access",
Long: `Re-login to the current cluster with read-write access.

This command is a helper that detects the currently logged-in cluster from your
kubeconfig and re-authenticates with read-write permissions. This is equivalent
to running 'ocm-backplane login --rw <cluster-id>' but without needing to specify
the cluster ID again.`,
Example: "ocm backplane rw",
RunE: runRw,
SilenceUsage: true,
}

func runRw(cmd *cobra.Command, argv []string) error {
logger.Debugln("Getting current cluster from kubeconfig")

// Get the current cluster from kubeconfig
clusterInfo, err := utils.DefaultClusterUtils.GetBackplaneClusterFromConfig()
if err != nil {
return fmt.Errorf("failed to get current cluster from kubeconfig: %w\nPlease make sure you are logged in to a cluster first", err)
}

logger.WithField("ClusterID", clusterInfo.ClusterID).Infoln("Re-logging in with read-write access")

// Set the --rw flag for login
err = login.LoginCmd.Flags().Set("rw", "true")
if err != nil {
return fmt.Errorf("failed to set rw flag: %w", err)
}

// Ensure we reset the flag after execution to avoid side effects
defer func() {
_ = login.LoginCmd.Flags().Set("rw", "false")
}()

// Execute login command with the current cluster ID
err = login.LoginCmd.RunE(cmd, []string{clusterInfo.ClusterID})
if err != nil {
return fmt.Errorf("failed to re-login with read-write access: %w", err)
}

fmt.Printf("Successfully re-logged in to cluster %s with read-write access\n", clusterInfo.ClusterID)

return nil
}