Skip to content
Open
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
33 changes: 26 additions & 7 deletions cmd/ocm-backplane/login/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ var (
clusterInfo bool
remediation string
govcloud bool
readonly bool
}

// loginType derive the login type based on flags and args
Expand Down Expand Up @@ -133,6 +134,12 @@ func init() {
"cluster-info",
false, "Print basic cluster information after login",
)
flags.BoolVar(
&args.readonly,
"readonly",
false,
"Login with read-only access to the cluster",
)
}

// TODO there is something about the proxy config in relation to overriding with --url
Expand Down Expand Up @@ -336,9 +343,10 @@ func runLogin(cmd *cobra.Command, argv []string) (err error) {
logger.WithFields(logger.Fields{
"bpURL": bpURL,
"clusterID": clusterID,
"readonly": args.readonly,
}).Debugln("Query backplane-api for proxy url of our target cluster")
// Query backplane-api for proxy url
bpAPIClusterURL, err := doLogin(bpURL, clusterID, *accessToken)
bpAPIClusterURL, err := doLoginWithConn(bpURL, clusterID, *accessToken, nil, args.readonly)
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 Expand Up @@ -474,7 +482,7 @@ func GetRestConfig(bp config.BackplaneConfiguration, clusterID string) (*rest.Co
return nil, err
}

bpAPIClusterURL, err := doLogin(bp.URL, clusterID, *accessToken)
bpAPIClusterURL, err := doLoginWithConn(bp.URL, clusterID, *accessToken, nil, false)
if err != nil {
return nil, fmt.Errorf("failed to backplane login to cluster %s: %v", cluster.Name(), err)
}
Expand Down Expand Up @@ -503,7 +511,7 @@ func GetRestConfigWithConn(bp config.BackplaneConfiguration, ocmConnection *ocms
return nil, err
}

bpAPIClusterURL, err := doLoginWithConn(bp.URL, clusterID, *accessToken, ocmConnection)
bpAPIClusterURL, err := doLoginWithConn(bp.URL, clusterID, *accessToken, ocmConnection, false)
if err != nil {
return nil, fmt.Errorf("failed to backplane login to cluster %s: %v", cluster.Name(), err)
}
Expand Down Expand Up @@ -559,10 +567,10 @@ func GetRestConfigAsUserWithConn(bp config.BackplaneConfiguration, ocmConn *ocms

// doLogin returns the proxy url for the target cluster.
func doLogin(api, clusterID, accessToken string) (string, error) {
return doLoginWithConn(api, clusterID, accessToken, nil)
return doLoginWithConn(api, clusterID, accessToken, nil, false)
}

func doLoginWithConn(api, clusterID, accessToken string, ocmConn *ocmsdk.Connection) (string, error) {
func doLoginWithConn(api, clusterID, accessToken string, ocmConn *ocmsdk.Connection, readonly bool) (string, error) {
var client BackplaneApi.ClientInterface
var err error = nil
if ocmConn != nil {
Expand All @@ -574,7 +582,18 @@ func doLoginWithConn(api, clusterID, accessToken string, ocmConn *ocmsdk.Connect
return "", fmt.Errorf("unable to create backplane api client")
}

resp, err := client.LoginCluster(context.TODO(), clusterID)
// Create request editor to add readonly query parameter if needed
var reqEditors []BackplaneApi.RequestEditorFn
if readonly {
reqEditors = append(reqEditors, func(ctx context.Context, req *http.Request) error {
q := req.URL.Query()
q.Add("readonly", "true")
req.URL.RawQuery = q.Encode()
return nil
})
}

resp, err := client.LoginCluster(context.TODO(), clusterID, reqEditors...)
// Print the whole response if we can't parse it. Eg. 5xx error from http server.
if err != nil {
// trying to determine the error
Expand Down Expand Up @@ -732,4 +751,4 @@ func getClusterIDFromExistingKubeConfig() (string, error) {
clusterKey = clusterInfo.ClusterID
logger.Debugf("Backplane Cluster Infromation data extracted: %+v\n", clusterInfo)
return clusterKey, nil
}
}
76 changes: 75 additions & 1 deletion cmd/ocm-backplane/login/login_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,16 @@ import (
"path/filepath"
"strings"

"go.uber.org/mock/gomock"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1"
"github.com/trivago/tgo/tcontainer"
"go.uber.org/mock/gomock"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/tools/clientcmd/api"

"github.com/andygrunwald/go-jira"
BackplaneApi "github.com/openshift/backplane-api/pkg/client"
"github.com/openshift/backplane-cli/pkg/backplaneapi"
backplaneapiMock "github.com/openshift/backplane-cli/pkg/backplaneapi/mocks"
"github.com/openshift/backplane-cli/pkg/cli/config"
Expand Down Expand Up @@ -684,4 +685,77 @@ var _ = Describe("Login command", func() {
Expect(err.Error()).To(Equal("clusterID cannot be detected for JIRA issue:OHSS-1000"))
})
})

Context("readonly flag functionality", func() {
BeforeEach(func() {
err := utils.CreateTempKubeConfig(nil)
Expect(err).To(BeNil())
})

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

mockOcmInterface.EXPECT().GetOCMEnvironment().Return(ocmEnv, nil).AnyTimes()
mockOcmInterface.EXPECT().GetTargetCluster(testClusterID).Return(trueClusterID, trueClusterID, nil).Times(1)
mockOcmInterface.EXPECT().GetOCMAccessToken().Return(&testToken, nil).Times(1)
mockOcmInterface.EXPECT().IsClusterHibernating(gomock.Eq(trueClusterID)).Return(false, nil).Times(1)
mockClientUtil.EXPECT().MakeRawBackplaneAPIClientWithAccessToken(backplaneAPIURI, testToken).Return(mockClient, nil)

// Mock LoginCluster and capture the request to verify readonly query param
var capturedURL string
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(trueClusterID), gomock.Any()).DoAndReturn(
func(ctx interface{}, clusterId string, reqEditors ...interface{}) (*http.Response, error) {
// Create a mock request to test the editor
req, _ := http.NewRequest("GET", backplaneAPIURI+"/backplane/login/"+clusterId, nil)
// Apply the request editors if any
for _, editor := range reqEditors {
if fn, ok := editor.(BackplaneApi.RequestEditorFn); ok {
_ = fn(nil, req)
}
}
capturedURL = req.URL.String()
return fakeResp, nil
},
)

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

Expect(err).To(BeNil())
// Verify readonly=true query parameter is present in the URL
Expect(capturedURL).To(ContainSubstring("readonly=true"))
})

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

mockOcmInterface.EXPECT().GetOCMEnvironment().Return(ocmEnv, nil).AnyTimes()
mockOcmInterface.EXPECT().GetTargetCluster(testClusterID).Return(trueClusterID, trueClusterID, nil).Times(1)
mockOcmInterface.EXPECT().GetOCMAccessToken().Return(&testToken, nil).Times(1)
mockOcmInterface.EXPECT().IsClusterHibernating(gomock.Eq(trueClusterID)).Return(false, nil).Times(1)
mockClientUtil.EXPECT().MakeRawBackplaneAPIClientWithAccessToken(backplaneAPIURI, testToken).Return(mockClient, nil)

// Mock LoginCluster and capture the request
var capturedURL string
mockClient.EXPECT().LoginCluster(gomock.Any(), gomock.Eq(trueClusterID)).DoAndReturn(
func(ctx interface{}, clusterId string, reqEditors ...interface{}) (*http.Response, error) {
// Create a mock request
req, _ := http.NewRequest("GET", backplaneAPIURI+"/backplane/login/"+clusterId, nil)
capturedURL = req.URL.String()
return fakeResp, nil
},
)

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

Expect(err).To(BeNil())
// Verify readonly query parameter is not present
Expect(capturedURL).NotTo(ContainSubstring("readonly"))
})
})
})