Skip to content

Commit cf2b358

Browse files
authored
OSD-28241: Add multi-environment OCM and backplane connection support (#828)
* OSD-28241: Add multi-environment OCM and backplane connection support * OSD-28241: Lint updates. Test updates. * OSD-28241: Make generate-docs * OSD-28241: Remove manual integration test command * OSD-28241: Regenerate docs with removal of test command * OSD-28241: Update errorf err formatting. Update getOCMConfigLocation return empty string upon err.
1 parent a668eb0 commit cf2b358

File tree

7 files changed

+1147
-3
lines changed

7 files changed

+1147
-3
lines changed

cmd/common/helpers.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"io"
77

8+
ocmsdk "github.com/openshift-online/ocm-sdk-go"
89
bplogin "github.com/openshift/backplane-cli/cmd/ocm-backplane/login"
910
bpconfig "github.com/openshift/backplane-cli/pkg/cli/config"
1011
"github.com/openshift/osdctl/pkg/utils"
@@ -76,3 +77,31 @@ func GetKubeConfigAndClient(clusterID string, elevationReasons ...string) (clien
7677
}
7778
return kubeCli, kubeconfig, clientset, err
7879
}
80+
81+
// If some elevationReasons are provided, then the config will be elevated with user backplane-cluster-admin
82+
// Using provided OCM sdk connection for config values.
83+
func GetKubeConfigAndClientWithConn(clusterID string, ocm *ocmsdk.Connection, elevationReasons ...string) (client.Client, *rest.Config, *kubernetes.Clientset, error) {
84+
bp, err := bpconfig.GetBackplaneConfigurationWithConn(ocm)
85+
if err != nil {
86+
return nil, nil, nil, fmt.Errorf("failed to load backplane-cli config: %w", err)
87+
}
88+
var kubeconfig *rest.Config
89+
if len(elevationReasons) == 0 {
90+
kubeconfig, err = bplogin.GetRestConfigWithConn(bp, ocm, clusterID)
91+
} else {
92+
kubeconfig, err = bplogin.GetRestConfigAsUserWithConn(bp, ocm, clusterID, "backplane-cluster-admin", elevationReasons...)
93+
}
94+
if err != nil {
95+
return nil, nil, nil, err
96+
}
97+
// create the clientset
98+
clientset, err := kubernetes.NewForConfig(kubeconfig)
99+
if err != nil {
100+
return nil, nil, nil, err
101+
}
102+
kubeCli, err := client.New(kubeconfig, client.Options{})
103+
if err != nil {
104+
return nil, nil, nil, err
105+
}
106+
return kubeCli, kubeconfig, clientset, nil
107+
}

cmd/common/helpers_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package common
2+
3+
import (
4+
"testing"
5+
6+
sdk "github.com/openshift-online/ocm-sdk-go"
7+
)
8+
9+
// TestGetKubeConfigAndClientWithConn tests the GetKubeConfigAndClientWithConn function
10+
// which creates a Kubernetes client, REST config, and clientset using a provided OCM SDK
11+
// connection. This function supports both regular and elevated (backplane-cluster-admin)
12+
// access based on the presence of elevation reasons.
13+
func TestGetKubeConfigAndClientWithConn(t *testing.T) {
14+
tests := []struct {
15+
name string
16+
clusterID string
17+
ocmConn *sdk.Connection
18+
elevationReasons []string
19+
wantErr bool
20+
}{
21+
{
22+
// Test that passing a nil OCM connection without elevation returns an error
23+
name: "nil OCM connection",
24+
clusterID: "test-cluster-id",
25+
ocmConn: nil,
26+
elevationReasons: nil,
27+
wantErr: true,
28+
},
29+
{
30+
// Test that passing a nil OCM connection with elevation reasons also returns an error
31+
name: "nil OCM connection with elevation reasons",
32+
clusterID: "test-cluster-id",
33+
ocmConn: nil,
34+
elevationReasons: []string{"testing"},
35+
wantErr: true,
36+
},
37+
{
38+
// Test that passing an empty cluster ID with nil connection returns an error
39+
name: "empty cluster ID with nil connection",
40+
clusterID: "",
41+
ocmConn: nil,
42+
elevationReasons: nil,
43+
wantErr: true,
44+
},
45+
}
46+
47+
for _, tt := range tests {
48+
t.Run(tt.name, func(t *testing.T) {
49+
kubeCli, kubeconfig, clientset, err := GetKubeConfigAndClientWithConn(tt.clusterID, tt.ocmConn, tt.elevationReasons...)
50+
if tt.wantErr {
51+
if err == nil {
52+
t.Errorf("GetKubeConfigAndClientWithConn() expected error but got none")
53+
}
54+
} else {
55+
if err != nil {
56+
t.Errorf("GetKubeConfigAndClientWithConn() unexpected error = %v", err)
57+
}
58+
if kubeCli == nil {
59+
t.Errorf("GetKubeConfigAndClientWithConn() returned nil kubeCli")
60+
}
61+
if kubeconfig == nil {
62+
t.Errorf("GetKubeConfigAndClientWithConn() returned nil kubeconfig")
63+
}
64+
if clientset == nil {
65+
t.Errorf("GetKubeConfigAndClientWithConn() returned nil clientset")
66+
}
67+
}
68+
})
69+
}
70+
}

cmd/hive/cmd.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package hive
22

33
import (
44
"fmt"
5+
56
cd "github.com/openshift/osdctl/cmd/hive/clusterdeployment"
67
"github.com/spf13/cobra"
78
"k8s.io/cli-runtime/pkg/genericclioptions"

pkg/k8s/client.go

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"io"
77

8+
sdk "github.com/openshift-online/ocm-sdk-go"
89
bplogin "github.com/openshift/backplane-cli/cmd/ocm-backplane/login"
910
bpconfig "github.com/openshift/backplane-cli/pkg/cli/config"
1011
bputils "github.com/openshift/backplane-cli/pkg/utils"
@@ -162,10 +163,30 @@ func NewRestConfig(clusterID string) (*rest.Config, error) {
162163
return cfg, nil
163164
}
164165

166+
// Create Backplane connection to a provided cluster, using a provided ocm sdk connection
167+
// This is intended to allow backplane connections to multiple clusters which exist in different
168+
// ocm environments by allowing the caller to provide an ocm connection to the function.
169+
func NewWithConn(clusterID string, options client.Options, ocmConn *sdk.Connection) (client.Client, error) {
170+
if ocmConn == nil {
171+
return nil, fmt.Errorf("nil OCM sdk connection provided to NewWithConn()")
172+
}
173+
bp, err := bpconfig.GetBackplaneConfigurationWithConn(ocmConn)
174+
if err != nil {
175+
return nil, fmt.Errorf("failed to load backplane-cli config: %w", err)
176+
}
177+
178+
cfg, err := bplogin.GetRestConfigWithConn(bp, ocmConn, clusterID)
179+
if err != nil {
180+
return nil, err
181+
}
182+
setRuntimeLoggerDiscard()
183+
return client.New(cfg, options)
184+
}
185+
165186
func NewAsBackplaneClusterAdmin(clusterID string, options client.Options, elevationReasons ...string) (client.Client, error) {
166187
bp, err := bpconfig.GetBackplaneConfiguration()
167188
if err != nil {
168-
return nil, fmt.Errorf("failed to load backplane-cli config: %v", err)
189+
return nil, fmt.Errorf("failed to load backplane-cli config: %w", err)
169190
}
170191

171192
cfg, err := bplogin.GetRestConfigAsUser(bp, clusterID, "backplane-cluster-admin", elevationReasons...)
@@ -176,6 +197,26 @@ func NewAsBackplaneClusterAdmin(clusterID string, options client.Options, elevat
176197
return client.New(cfg, options)
177198
}
178199

200+
// Create Backplane connection as cluster admin to a provided cluster, using a provided ocm sdk connection
201+
// This is intended to allow backplane connections to multiple clusters which exist in different
202+
// ocm environments by allowing the caller to provide an ocm connection to the function.
203+
func NewAsBackplaneClusterAdminWithConn(clusterID string, options client.Options, ocmConn *sdk.Connection, elevationReasons ...string) (client.Client, error) {
204+
if ocmConn == nil {
205+
return nil, fmt.Errorf("nil OCM sdk connection provided to NewAsBackplaneClusterAdminWithConn()")
206+
}
207+
bp, err := bpconfig.GetBackplaneConfigurationWithConn(ocmConn)
208+
if err != nil {
209+
return nil, fmt.Errorf("failed to load backplane-cli config: %w", err)
210+
}
211+
212+
cfg, err := bplogin.GetRestConfigAsUserWithConn(bp, ocmConn, clusterID, "backplane-cluster-admin", elevationReasons...)
213+
if err != nil {
214+
return nil, err
215+
}
216+
setRuntimeLoggerDiscard()
217+
return client.New(cfg, options)
218+
}
219+
179220
func setRuntimeLoggerDiscard() {
180221
// To avoid warnings/backtrace, if k8s controller-runtime logger has not already been set, do it now...
181222
if !log.Log.Enabled() {

pkg/k8s/client_test.go

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package k8s
2+
3+
import (
4+
"testing"
5+
6+
sdk "github.com/openshift-online/ocm-sdk-go"
7+
"sigs.k8s.io/controller-runtime/pkg/client"
8+
)
9+
10+
// TestNewWithConn tests the NewWithConn function which creates a Kubernetes client
11+
// using backplane with a provided OCM SDK connection. This allows connecting to clusters
12+
// in different OCM environments by providing a custom OCM connection.
13+
func TestNewWithConn(t *testing.T) {
14+
tests := []struct {
15+
name string
16+
clusterID string
17+
options client.Options
18+
ocmConn *sdk.Connection
19+
wantErr bool
20+
}{
21+
{
22+
// Test that passing a nil OCM connection returns an error
23+
name: "nil OCM connection",
24+
clusterID: "test-cluster-id",
25+
options: client.Options{},
26+
ocmConn: nil,
27+
wantErr: true,
28+
},
29+
{
30+
// Test that passing an empty cluster ID with nil connection returns an error
31+
name: "empty cluster ID with nil connection",
32+
clusterID: "",
33+
options: client.Options{},
34+
ocmConn: nil,
35+
wantErr: true,
36+
},
37+
}
38+
39+
for _, tt := range tests {
40+
t.Run(tt.name, func(t *testing.T) {
41+
client, err := NewWithConn(tt.clusterID, tt.options, tt.ocmConn)
42+
if tt.wantErr {
43+
if err == nil {
44+
t.Errorf("NewWithConn() expected error but got none")
45+
}
46+
} else {
47+
if err != nil {
48+
t.Errorf("NewWithConn() unexpected error = %v", err)
49+
}
50+
if client == nil {
51+
t.Errorf("NewWithConn() returned nil client")
52+
}
53+
}
54+
})
55+
}
56+
}
57+
58+
// TestNewAsBackplaneClusterAdminWithConn tests the NewAsBackplaneClusterAdminWithConn function
59+
// which creates an elevated Kubernetes client (backplane-cluster-admin) using a provided OCM
60+
// SDK connection. This function allows connecting to clusters in different OCM environments
61+
// with elevated permissions.
62+
func TestNewAsBackplaneClusterAdminWithConn(t *testing.T) {
63+
tests := []struct {
64+
name string
65+
clusterID string
66+
options client.Options
67+
ocmConn *sdk.Connection
68+
elevationReasons []string
69+
wantErr bool
70+
}{
71+
{
72+
// Test that passing a nil OCM connection with elevation reasons returns an error
73+
name: "nil OCM connection",
74+
clusterID: "test-cluster-id",
75+
options: client.Options{},
76+
ocmConn: nil,
77+
elevationReasons: []string{"testing"},
78+
wantErr: true,
79+
},
80+
{
81+
// Test that passing a nil OCM connection without elevation reasons also returns an error
82+
name: "nil OCM connection with no elevation reasons",
83+
clusterID: "test-cluster-id",
84+
options: client.Options{},
85+
ocmConn: nil,
86+
elevationReasons: nil,
87+
wantErr: true,
88+
},
89+
{
90+
// Test that passing an empty cluster ID with nil connection returns an error
91+
name: "empty cluster ID with nil connection",
92+
clusterID: "",
93+
options: client.Options{},
94+
ocmConn: nil,
95+
elevationReasons: []string{"testing"},
96+
wantErr: true,
97+
},
98+
}
99+
100+
for _, tt := range tests {
101+
t.Run(tt.name, func(t *testing.T) {
102+
client, err := NewAsBackplaneClusterAdminWithConn(tt.clusterID, tt.options, tt.ocmConn, tt.elevationReasons...)
103+
if tt.wantErr {
104+
if err == nil {
105+
t.Errorf("NewAsBackplaneClusterAdminWithConn() expected error but got none")
106+
}
107+
} else {
108+
if err != nil {
109+
t.Errorf("NewAsBackplaneClusterAdminWithConn() unexpected error = %v", err)
110+
}
111+
if client == nil {
112+
t.Errorf("NewAsBackplaneClusterAdminWithConn() returned nil client")
113+
}
114+
}
115+
})
116+
}
117+
}

0 commit comments

Comments
 (0)