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
67 changes: 65 additions & 2 deletions cmd/cluster/resync.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ const (
)

type Resync struct {
hive client.Client
clusterId string
hive client.Client
clusterId string
hiveOcmUrl string
}

func newCmdResync() *cobra.Command {
Expand All @@ -44,13 +45,17 @@ func newCmdResync() *cobra.Command {
Example: `
# Force a cluster resync by deleting its clustersync CustomResource
osdctl cluster resync --cluster-id ${CLUSTER_ID}

# While connected to staging OCM, force a resync using the production Hive environment
OCM_URL=staging osdctl cluster resync --cluster-id ${CLUSTER_ID} --hive-ocm-url production
`,
RunE: func(cmd *cobra.Command, args []string) error {
return r.Run(context.Background())
},
}

resyncCmd.Flags().StringVarP(&r.clusterId, "cluster-id", "C", "", "OCM internal/external cluster id or cluster name to delete the clustersync for.")
resyncCmd.Flags().StringVar(&r.hiveOcmUrl, "hive-ocm-url", "", "(optional) OCM environment URL for Hive operations. Aliases: 'production', 'staging', 'integration'. This only changes how the Hive cluster is resolved; the target cluster still comes from the current/default OCM environment.")

return resyncCmd
}
Expand All @@ -67,6 +72,22 @@ func (r *Resync) New() error {
return err
}

// Validate and resolve --hive-ocm-url if provided
if r.hiveOcmUrl != "" {
resolvedHiveOcmURL, err := utils.ValidateAndResolveOcmUrl(r.hiveOcmUrl)
if err != nil {
return fmt.Errorf("invalid --hive-ocm-url: %w", err)
}
r.hiveOcmUrl = resolvedHiveOcmURL
}

// Check if multi-environment support is needed
if r.hiveOcmUrl != "" {
// Use new multi-environment path
return r.initWithMultiEnv(scheme)
}

// === ORIGINAL PATH (PRESERVED FOR BACKWARD COMPATIBILITY) ===
ocmClient, err := utils.CreateConnection()
if err != nil {
return err
Expand All @@ -93,6 +114,48 @@ func (r *Resync) New() error {
return nil
}

// initWithMultiEnv initializes the Resync struct using separate OCM connections for target cluster and hive
func (r *Resync) initWithMultiEnv(scheme *runtime.Scheme) error {
// Create OCM connection for target cluster (uses system env vars)
targetOCM, err := utils.CreateConnection()
if err != nil {
return fmt.Errorf("failed to create target cluster OCM connection: %w", err)
}
defer targetOCM.Close()

// Create separate OCM connection for hive
hiveOCM, err := utils.CreateConnectionWithUrl(r.hiveOcmUrl)
if err != nil {
return fmt.Errorf("failed to create hive OCM connection with URL '%s': %w", r.hiveOcmUrl, err)
}
defer hiveOCM.Close()

// Get cluster info from target OCM environment
cluster, err := utils.GetClusterAnyStatus(targetOCM, r.clusterId)
if err != nil {
return fmt.Errorf("failed to get OCM cluster info for %s: %w", r.clusterId, err)
}
r.clusterId = cluster.ID()

// Get hive cluster using both OCM connections
hive, err := utils.GetHiveClusterWithConn(cluster.ID(), targetOCM, hiveOCM)
if err != nil {
return fmt.Errorf("failed to get hive cluster (OCM URL:'%s'): %w", r.hiveOcmUrl, err)
}

// Create k8s client for hive using the hive OCM connection
hc, err := k8s.NewWithConn(hive.ID(), client.Options{Scheme: scheme}, hiveOCM)
if err != nil {
return fmt.Errorf("failed to create hive k8s client(OCM URL:'%s'): %w", r.hiveOcmUrl, err)
}

r.hive = hc
log.Printf("ready to delete clustersync for cluster: %s/%s on hive: %s (using hive OCM URL: %s)",
cluster.ID(), cluster.Name(), hive.Name(), r.hiveOcmUrl)

return nil
}

func (r *Resync) Run(ctx context.Context) error {
if err := r.New(); err != nil {
return fmt.Errorf("failed to initialize command: %v", err)
Expand Down
72 changes: 72 additions & 0 deletions cmd/cluster/resync_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package cluster

import (
"strings"
"testing"

"github.com/openshift/osdctl/pkg/utils"
)

// TestHiveOcmUrlValidation tests the early validation of --hive-ocm-url flag in the resync command
func TestHiveOcmUrlValidation(t *testing.T) {
tests := []struct {
name string
hiveOcmUrl string
expectErr bool
errContains string
}{
{
name: "Valid hive-ocm-url (production)",
hiveOcmUrl: "production",
expectErr: false,
},
{
name: "Valid hive-ocm-url (staging)",
hiveOcmUrl: "staging",
expectErr: false,
},
{
name: "Valid hive-ocm-url (integration)",
hiveOcmUrl: "integration",
expectErr: false,
},
{
name: "Valid hive-ocm-url (full URL)",
hiveOcmUrl: "https://api.openshift.com",
expectErr: false,
},
{
name: "Invalid hive-ocm-url",
hiveOcmUrl: "invalid-environment",
expectErr: true,
errContains: "invalid OCM_URL",
},
{
name: "Empty hive-ocm-url (flag omitted)",
hiveOcmUrl: "",
expectErr: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Mirrors Resync.New(): validate only when flag is provided
var err error
if tt.hiveOcmUrl != "" {
_, err = utils.ValidateAndResolveOcmUrl(tt.hiveOcmUrl)
}

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 @@ -2009,6 +2009,7 @@ osdctl cluster resync [flags]
-C, --cluster-id string OCM internal/external cluster id or cluster name to delete the clustersync for.
--context string The name of the kubeconfig context to use
-h, --help help for resync
--hive-ocm-url string (optional) OCM environment URL for Hive operations. Aliases: 'production', 'staging', 'integration'. This only changes how the Hive cluster is resolved; the target cluster still comes from the current/default OCM environment.
--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
8 changes: 6 additions & 2 deletions docs/osdctl_cluster_resync.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,17 @@ osdctl cluster resync [flags]
# Force a cluster resync by deleting its clustersync CustomResource
osdctl cluster resync --cluster-id ${CLUSTER_ID}

# While connected to staging OCM, force a resync using the production Hive environment
OCM_URL=staging osdctl cluster resync --cluster-id ${CLUSTER_ID} --hive-ocm-url production

```

### Options

```
-C, --cluster-id string OCM internal/external cluster id or cluster name to delete the clustersync for.
-h, --help help for resync
-C, --cluster-id string OCM internal/external cluster id or cluster name to delete the clustersync for.
-h, --help help for resync
--hive-ocm-url string (optional) OCM environment URL for Hive operations. Aliases: 'production', 'staging', 'integration'. This only changes how the Hive cluster is resolved; the target cluster still comes from the current/default OCM environment.
```

### Options inherited from parent commands
Expand Down