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
66 changes: 51 additions & 15 deletions cmd/cluster/access/access.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ func NewCmdAccess(streams genericclioptions.IOStreams, client *k8s.LazyClient) *
accessCmd.AddCommand(newCmdCleanup(client, streams))
accessCmd.Flags().StringVar(&ops.reason, "reason", "", "The reason for this command, which requires elevation, to be run (usualy an OHSS or PD ticket)")
accessCmd.Flags().StringVarP(&ops.clusterID, "cluster-id", "C", "", "Provide the internal ID of the cluster")
accessCmd.Flags().StringVar(&ops.hiveOcmUrl, "hive-ocm-url", "", "(optional) OCM environment URL for hive operations. Aliases: 'production', 'staging', 'integration'. If not specified, uses the same OCM environment as the target cluster.")
_ = accessCmd.MarkFlagRequired("reason")
_ = accessCmd.MarkFlagRequired("cluster-id")

Expand All @@ -74,8 +75,9 @@ func NewCmdAccess(streams genericclioptions.IOStreams, client *k8s.LazyClient) *

// clusterAccessOptions contains the objects and information required to access a cluster
type clusterAccessOptions struct {
reason string
clusterID string
reason string
clusterID string
hiveOcmUrl string

genericclioptions.IOStreams
}
Expand Down Expand Up @@ -112,23 +114,26 @@ func (c *clusterAccessOptions) Readln() (string, error) {

// accessCmdComplete verifies the command's invocation, returning an error if the usage is invalid
func (c *clusterAccessOptions) accessCmdComplete() error {
return osdctlutil.IsValidClusterKey(c.clusterID)
}

// Run executes the 'break-glass' access subcommand
func (c *clusterAccessOptions) Run(ctx context.Context) error {
// Login to hive shard
hive, err := osdctlutil.GetHiveCluster(c.clusterID)
if err != nil {
return fmt.Errorf("failed to retrieve hive shard for %q: %w", c.clusterID, err)
if err := osdctlutil.IsValidClusterKey(c.clusterID); err != nil {
return err
}

hiveClient, err := k8s.NewAsBackplaneClusterAdmin(hive.ID(), kclient.Options{Scheme: scheme.Scheme}, c.reason, fmt.Sprintf("Elevation required to break-glass on %q cluster", c.clusterID))
if err != nil {
return fmt.Errorf("failed to login to hive shard %q: %w", hive.Name(), err)
// Validate --hive-ocm-url if provided
if c.hiveOcmUrl != "" {
_, err := osdctlutil.ValidateAndResolveOcmUrl(c.hiveOcmUrl)
if err != nil {
return fmt.Errorf("invalid --hive-ocm-url: %w", err)
}
}

c.Println(fmt.Sprintf("Retrieving Kubeconfig for cluster '%s'", c.clusterID))
return nil
}

// Run executes the 'break-glass' access subcommand
func (c *clusterAccessOptions) Run(ctx context.Context) error {
var hive *clustersmgmtv1.Cluster
var hiveClient kclient.Client
var err error

// Connect to ocm and grab cluster definition: user-provided cluster identifier could be any one of name, internal ID, or UUID
// and we need to ensure we're only referring to cluster by internal-ID while interacting with hive
Expand All @@ -145,6 +150,37 @@ func (c *clusterAccessOptions) Run(ctx context.Context) error {
return err
}
c.Println(fmt.Sprintf("Internal Cluster ID: %s", cluster.ID()))
c.Println(fmt.Sprintf("Retrieving Kubeconfig for cluster '%s'", c.clusterID))

if c.hiveOcmUrl != "" {
// Multi-environment path - enables staging/integration testing
hiveOCM, err := osdctlutil.CreateConnectionWithUrl(c.hiveOcmUrl)
if err != nil {
return fmt.Errorf("failed to create hive OCM connection with URL '%s': %w", c.hiveOcmUrl, err)
}
defer hiveOCM.Close()

hive, err = osdctlutil.GetHiveClusterWithConn(cluster.ID(), conn, hiveOCM)
if err != nil {
return fmt.Errorf("failed to retrieve hive shard for %q (OCM URL:'%s'): %w", c.clusterID, c.hiveOcmUrl, err)
}

hiveClient, err = k8s.NewAsBackplaneClusterAdminWithConn(hive.ID(), kclient.Options{Scheme: scheme.Scheme}, hiveOCM, c.reason, fmt.Sprintf("Elevation required to break-glass on %q cluster", c.clusterID))
if err != nil {
return fmt.Errorf("failed to login to hive shard %q (OCM URL:'%s'): %w", hive.Name(), c.hiveOcmUrl, err)
}
} else {
// Original path - backward compatible
hive, err = osdctlutil.GetHiveCluster(cluster.ID())
if err != nil {
return fmt.Errorf("failed to retrieve hive shard for %q: %w", c.clusterID, err)
}

hiveClient, err = k8s.NewAsBackplaneClusterAdmin(hive.ID(), kclient.Options{Scheme: scheme.Scheme}, c.reason, fmt.Sprintf("Elevation required to break-glass on %q cluster", c.clusterID))
if err != nil {
return fmt.Errorf("failed to login to hive shard %q: %w", hive.Name(), err)
}
}

// Retrieve the kubeconfig secret from the cluster's namespace on hive
ns, err := getClusterNamespace(hiveClient, cluster.ID())
Expand Down
79 changes: 79 additions & 0 deletions cmd/cluster/access/access_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -438,3 +438,82 @@ func TestGetKubeConfigSecret(t *testing.T) {
})
}
}

// TestClusterAccessOptions_accessCmdComplete tests the early validation of cluster-id and hive-ocm-url
func TestClusterAccessOptions_accessCmdComplete(t *testing.T) {
tests := []struct {
name string
clusterID string
hiveOcmUrl string
expectErr bool
errContains string
}{
{
name: "Valid cluster ID, no hive-ocm-url",
clusterID: "test-cluster-123",
hiveOcmUrl: "",
expectErr: false,
},
{
name: "Valid cluster ID, valid hive-ocm-url (production)",
clusterID: "test-cluster-123",
hiveOcmUrl: "production",
expectErr: false,
},
{
name: "Valid cluster ID, valid hive-ocm-url (staging)",
clusterID: "test-cluster-123",
hiveOcmUrl: "staging",
expectErr: false,
},
{
name: "Valid cluster ID, valid hive-ocm-url (integration)",
clusterID: "test-cluster-123",
hiveOcmUrl: "integration",
expectErr: false,
},
{
name: "Valid cluster ID, valid hive-ocm-url (full URL)",
clusterID: "test-cluster-123",
hiveOcmUrl: "https://api.openshift.com",
expectErr: false,
},
{
name: "Valid cluster ID, invalid hive-ocm-url",
clusterID: "test-cluster-123",
hiveOcmUrl: "invalid-environment",
expectErr: true,
errContains: "invalid --hive-ocm-url",
},
{
name: "Empty cluster ID",
clusterID: "",
hiveOcmUrl: "",
expectErr: true,
errContains: "isn't valid",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &clusterAccessOptions{
clusterID: tt.clusterID,
hiveOcmUrl: tt.hiveOcmUrl,
}

err := c.accessCmdComplete()

if tt.expectErr {
if err == nil {
t.Errorf("Expected error containing '%s', but got nil", tt.errContains)
} else if !strings.Contains(err.Error(), tt.errContains) {
t.Errorf("Expected error containing '%s', but got: %v", tt.errContains, err)
}
} else {
if err != nil {
t.Errorf("Expected no error, but got: %v", err)
}
}
})
}
}
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1224,6 +1224,7 @@ osdctl cluster break-glass --cluster-id <cluster-identifier> [flags]
-C, --cluster-id string Provide the internal ID of the cluster
--context string The name of the kubeconfig context to use
-h, --help help for break-glass
--hive-ocm-url string (optional) OCM environment URL for hive operations. Aliases: 'production', 'staging', 'integration'. If not specified, uses the same OCM environment as the target cluster.
--insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
--kubeconfig string Path to the kubeconfig file to use for CLI requests.
-o, --output string Valid formats are ['', 'json', 'yaml', 'env']
Expand Down
7 changes: 4 additions & 3 deletions docs/osdctl_cluster_break-glass.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ osdctl cluster break-glass --cluster-id <cluster-identifier> [flags]
### Options

```
-C, --cluster-id string Provide the internal ID of the cluster
-h, --help help for break-glass
--reason string The reason for this command, which requires elevation, to be run (usualy an OHSS or PD ticket)
-C, --cluster-id string Provide the internal ID of the cluster
-h, --help help for break-glass
--hive-ocm-url string (optional) OCM environment URL for hive operations. Aliases: 'production', 'staging', 'integration'. If not specified, uses the same OCM environment as the target cluster.
--reason string The reason for this command, which requires elevation, to be run (usualy an OHSS or PD ticket)
```

### Options inherited from parent commands
Expand Down