From 87ea7488296960e28be4dd196dabf57ec99ed2f9 Mon Sep 17 00:00:00 2001 From: Claudio Busse Date: Fri, 17 May 2024 18:18:50 +0200 Subject: [PATCH] OSD-23312: Refactor GetRestConfig() to allow passing an ocm connection programatically --- cmd/ocm-backplane/login/login.go | 33 ++++++++++++++++++++++ cmd/ocm-backplane/login/login_test.go | 1 - pkg/ocm/mocks/ocmWrapperMock.go | 30 ++++++++++++++++++++ pkg/ocm/ocmWrapper.go | 40 ++++++++++++++++++--------- 4 files changed, 90 insertions(+), 14 deletions(-) diff --git a/cmd/ocm-backplane/login/login.go b/cmd/ocm-backplane/login/login.go index 6fee5e84..c7958841 100644 --- a/cmd/ocm-backplane/login/login.go +++ b/cmd/ocm-backplane/login/login.go @@ -20,6 +20,7 @@ import ( BackplaneApi "github.com/openshift/backplane-api/pkg/client" + ocmsdk "github.com/openshift-online/ocm-sdk-go" "github.com/openshift/backplane-cli/pkg/backplaneapi" "github.com/openshift/backplane-cli/pkg/cli/config" "github.com/openshift/backplane-cli/pkg/cli/globalflags" @@ -388,6 +389,38 @@ func GetRestConfig(bp config.BackplaneConfiguration, clusterID string) (*rest.Co return cfg, nil } +// GetRestConfig returns a client-go *rest.Config which can be used to programmatically interact with the +// Kubernetes API of a provided clusterID +func GetRestConfigWithConn(bp config.BackplaneConfiguration, ocmConnection *ocmsdk.Connection, clusterID string) (*rest.Config, error) { + cluster, err := ocm.DefaultOCMInterface.GetClusterInfoByIDWithConn(ocmConnection, clusterID) + if err != nil { + return nil, err + } + + accessToken, err := ocm.DefaultOCMInterface.GetOCMAccessTokenWithConn(ocmConnection) + if err != nil { + return nil, err + } + + bpAPIClusterURL, err := doLogin(bp.URL, clusterID, *accessToken) + if err != nil { + return nil, fmt.Errorf("failed to backplane login to cluster %s: %v", cluster.Name(), err) + } + + cfg := &rest.Config{ + Host: bpAPIClusterURL, + BearerToken: *accessToken, + } + + if bp.ProxyURL != nil { + cfg.Proxy = func(*http.Request) (*url.URL, error) { + return url.Parse(*bp.ProxyURL) + } + } + + return cfg, nil +} + // GetRestConfigAsUser returns a client-go *rest.Config like GetRestConfig, but supports configuring an // impersonation username. Commonly, this is "backplane-cluster-admin" // best practice would be to add at least one elevationReason in order to justity the impersonation diff --git a/cmd/ocm-backplane/login/login_test.go b/cmd/ocm-backplane/login/login_test.go index 326e6701..a7c77bec 100644 --- a/cmd/ocm-backplane/login/login_test.go +++ b/cmd/ocm-backplane/login/login_test.go @@ -502,7 +502,6 @@ var _ = Describe("Login command", func() { }) Context("check GetRestConfigAsUser", func() { - It("check config creation with username and without elevationReasons", func() { mockOcmInterface.EXPECT().GetClusterInfoByID(testClusterID).Return(mockCluster, nil) mockOcmInterface.EXPECT().GetOCMAccessToken().Return(&testToken, nil) diff --git a/pkg/ocm/mocks/ocmWrapperMock.go b/pkg/ocm/mocks/ocmWrapperMock.go index ce84ef2a..6546d6ee 100644 --- a/pkg/ocm/mocks/ocmWrapperMock.go +++ b/pkg/ocm/mocks/ocmWrapperMock.go @@ -50,6 +50,21 @@ func (mr *MockOCMInterfaceMockRecorder) GetClusterInfoByID(arg0 interface{}) *go return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClusterInfoByID", reflect.TypeOf((*MockOCMInterface)(nil).GetClusterInfoByID), arg0) } +// GetClusterInfoByIDWithConn mocks base method. +func (m *MockOCMInterface) GetClusterInfoByIDWithConn(arg0 *sdk.Connection, arg1 string) (*v1.Cluster, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetClusterInfoByIDWithConn", arg0, arg1) + ret0, _ := ret[0].(*v1.Cluster) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetClusterInfoByIDWithConn indicates an expected call of GetClusterInfoByIDWithConn. +func (mr *MockOCMInterfaceMockRecorder) GetClusterInfoByIDWithConn(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClusterInfoByIDWithConn", reflect.TypeOf((*MockOCMInterface)(nil).GetClusterInfoByIDWithConn), arg0, arg1) +} + // GetManagingCluster mocks base method. func (m *MockOCMInterface) GetManagingCluster(arg0 string) (string, string, bool, error) { m.ctrl.T.Helper() @@ -82,6 +97,21 @@ func (mr *MockOCMInterfaceMockRecorder) GetOCMAccessToken() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOCMAccessToken", reflect.TypeOf((*MockOCMInterface)(nil).GetOCMAccessToken)) } +// GetOCMAccessTokenWithConn mocks base method. +func (m *MockOCMInterface) GetOCMAccessTokenWithConn(arg0 *sdk.Connection) (*string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetOCMAccessTokenWithConn", arg0) + ret0, _ := ret[0].(*string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetOCMAccessTokenWithConn indicates an expected call of GetOCMAccessTokenWithConn. +func (mr *MockOCMInterfaceMockRecorder) GetOCMAccessTokenWithConn(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOCMAccessTokenWithConn", reflect.TypeOf((*MockOCMInterface)(nil).GetOCMAccessTokenWithConn), arg0) +} + // GetOCMEnvironment mocks base method. func (m *MockOCMInterface) GetOCMEnvironment() (*v1.Environment, error) { m.ctrl.T.Helper() diff --git a/pkg/ocm/ocmWrapper.go b/pkg/ocm/ocmWrapper.go index 4793f0bc..4c33b514 100644 --- a/pkg/ocm/ocmWrapper.go +++ b/pkg/ocm/ocmWrapper.go @@ -27,6 +27,8 @@ type OCMInterface interface { GetPullSecret() (string, error) GetStsSupportJumpRoleARN(ocmConnection *ocmsdk.Connection, clusterID string) (string, error) GetOCMEnvironment() (*cmv1.Environment, error) + GetOCMAccessTokenWithConn(ocmConnection *ocmsdk.Connection) (*string, error) + GetClusterInfoByIDWithConn(ocmConnection *ocmsdk.Connection, clusterID string) (*cmv1.Cluster, error) } const ( @@ -38,7 +40,7 @@ type DefaultOCMInterfaceImpl struct{} var DefaultOCMInterface OCMInterface = &DefaultOCMInterfaceImpl{} // IsClusterHibernating returns a boolean to indicate whether the cluster is hibernating -func (*DefaultOCMInterfaceImpl) IsClusterHibernating(clusterID string) (bool, error) { +func (o *DefaultOCMInterfaceImpl) IsClusterHibernating(clusterID string) (bool, error) { connection, err := ocm.NewConnection().Build() if err != nil { return false, fmt.Errorf("failed to create OCM connection: %v", err) @@ -54,7 +56,7 @@ func (*DefaultOCMInterfaceImpl) IsClusterHibernating(clusterID string) (bool, er } // GetTargetCluster returns one single cluster based on the search key and survery. -func (*DefaultOCMInterfaceImpl) GetTargetCluster(clusterKey string) (clusterID, clusterName string, err error) { +func (o *DefaultOCMInterfaceImpl) GetTargetCluster(clusterKey string) (clusterID, clusterName string, err error) { // Create the client for the OCM API: connection, err := ocm.NewConnection().Build() if err != nil { @@ -143,7 +145,7 @@ func (o *DefaultOCMInterfaceImpl) GetManagingCluster(targetClusterID string) (cl } // GetServiceCluster gets the service cluster for a given hpyershift hosted cluster -func (*DefaultOCMInterfaceImpl) GetServiceCluster(targetClusterID string) (clusterID, clusterName string, err error) { +func (o *DefaultOCMInterfaceImpl) GetServiceCluster(targetClusterID string) (clusterID, clusterName string, err error) { // Create the client for the OCM API connection, err := ocm.NewConnection().Build() if err != nil { @@ -197,15 +199,22 @@ func (*DefaultOCMInterfaceImpl) GetServiceCluster(targetClusterID string) (clust } // GetOCMAccessToken initiates the OCM connection and returns the access token -func (*DefaultOCMInterfaceImpl) GetOCMAccessToken() (*string, error) { +func (o *DefaultOCMInterfaceImpl) GetOCMAccessToken() (*string, error) { // Get ocm access token - logger.Debugln("Finding ocm token") connection, err := ocm.NewConnection().Build() if err != nil { return nil, fmt.Errorf("failed to create OCM connection: %v", err) } defer connection.Close() - accessToken, _, err := connection.Tokens() + + return o.GetOCMAccessTokenWithConn(connection) +} + +// GetOCMAccessTokenWithConn returns the access token of an ocmConnection +func (o *DefaultOCMInterfaceImpl) GetOCMAccessTokenWithConn(ocmConnection *ocmsdk.Connection) (*string, error) { + // Get ocm access token + logger.Debugln("Finding ocm token") + accessToken, _, err := ocmConnection.Tokens() if err != nil { return nil, err } @@ -217,7 +226,7 @@ func (*DefaultOCMInterfaceImpl) GetOCMAccessToken() (*string, error) { } // GetPullSecret returns pull secret from OCM -func (*DefaultOCMInterfaceImpl) GetPullSecret() (string, error) { +func (o *DefaultOCMInterfaceImpl) GetPullSecret() (string, error) { // Get ocm access token logger.Debugln("Finding ocm token") @@ -239,7 +248,7 @@ func (*DefaultOCMInterfaceImpl) GetPullSecret() (string, error) { // GetClusterInfoByID calls the OCM to retrieve the cluster info // for a given internal cluster id. -func (*DefaultOCMInterfaceImpl) GetClusterInfoByID(clusterID string) (*cmv1.Cluster, error) { +func (o *DefaultOCMInterfaceImpl) GetClusterInfoByID(clusterID string) (*cmv1.Cluster, error) { // Create the client for the OCM API: connection, err := ocm.NewConnection().Build() if err != nil { @@ -247,8 +256,13 @@ func (*DefaultOCMInterfaceImpl) GetClusterInfoByID(clusterID string) (*cmv1.Clus } defer connection.Close() - // Get the cluster info based on the cluster id - response, err := connection.ClustersMgmt().V1().Clusters().Cluster(clusterID).Get().Send() + return o.GetClusterInfoByIDWithConn(connection, clusterID) +} + +// GetClusterInfoByIDWithConn calls the OCM to retrieve the cluster info +// for a given internal cluster id. +func (o *DefaultOCMInterfaceImpl) GetClusterInfoByIDWithConn(ocmConnection *ocmsdk.Connection, clusterID string) (*cmv1.Cluster, error) { + response, err := ocmConnection.ClustersMgmt().V1().Clusters().Cluster(clusterID).Get().Send() if err != nil { return nil, fmt.Errorf("can't retrieve cluster for id '%s': %v", clusterID, err) } @@ -260,7 +274,7 @@ func (*DefaultOCMInterfaceImpl) GetClusterInfoByID(clusterID string) (*cmv1.Clus } // IsProduction checks if OCM is currently in production env -func (*DefaultOCMInterfaceImpl) IsProduction() (bool, error) { +func (o *DefaultOCMInterfaceImpl) IsProduction() (bool, error) { // Create the client for the OCM API: connection, err := ocm.NewConnection().Build() if err != nil { @@ -271,7 +285,7 @@ func (*DefaultOCMInterfaceImpl) IsProduction() (bool, error) { return connection.URL() == "https://api.openshift.com", nil } -func (*DefaultOCMInterfaceImpl) GetStsSupportJumpRoleARN(ocmConnection *ocmsdk.Connection, clusterID string) (string, error) { +func (o *DefaultOCMInterfaceImpl) GetStsSupportJumpRoleARN(ocmConnection *ocmsdk.Connection, clusterID string) (string, error) { response, err := ocmConnection.ClustersMgmt().V1().Clusters().Cluster(clusterID).StsSupportJumpRole().Get().Send() if err != nil { return "", fmt.Errorf("failed to get STS Support Jump Role for cluster %v, %w", clusterID, err) @@ -280,7 +294,7 @@ func (*DefaultOCMInterfaceImpl) GetStsSupportJumpRoleARN(ocmConnection *ocmsdk.C } // GetBackplaneURL returns the Backplane API URL based on the OCM env -func (*DefaultOCMInterfaceImpl) GetOCMEnvironment() (*cmv1.Environment, error) { +func (o *DefaultOCMInterfaceImpl) GetOCMEnvironment() (*cmv1.Environment, error) { // Create the client for the OCM API connection, err := ocm.NewConnection().Build() if err != nil {