From b957d6acb979f259419d825bcb89635fe093c433 Mon Sep 17 00:00:00 2001 From: Bryan Florkiewicz Date: Mon, 13 Dec 2021 16:37:03 -0500 Subject: [PATCH] CONSOLE 2919: code review adjustments for managed cluster action/views Addresses @jhadvig comments on https://github.com/openshift/console-operator/pull/602. --- ...anaged-cluster-ingress-cert-configmap.yaml | 2 +- pkg/console/assets/bindata.go | 2 +- .../controllers/managedclusters/controller.go | 185 +++++++++--------- pkg/console/operator/sync_v400.go | 15 +- .../subresource/configmap/configmap.go | 10 +- .../subresource/configmap/configmap_test.go | 110 ++++++++--- .../configmap/managed_clusters_test.go | 2 +- .../subresource/consoleserver/types.go | 2 +- .../default_ingress_cert.go} | 2 +- .../oauth_client.go} | 43 +++- .../managedclusteraction/oauth-client.go | 45 ----- 11 files changed, 233 insertions(+), 185 deletions(-) rename pkg/console/subresource/{managedclusterview/default-ingress-cert.go => managedcluster/default_ingress_cert.go} (98%) rename pkg/console/subresource/{managedclusterview/oauth-client.go => managedcluster/oauth_client.go} (64%) delete mode 100644 pkg/console/subresource/managedclusteraction/oauth-client.go diff --git a/bindata/configmaps/console-managed-cluster-ingress-cert-configmap.yaml b/bindata/configmaps/console-managed-cluster-ingress-cert-configmap.yaml index f39c2d0e06..fd38c7b43c 100644 --- a/bindata/configmaps/console-managed-cluster-ingress-cert-configmap.yaml +++ b/bindata/configmaps/console-managed-cluster-ingress-cert-configmap.yaml @@ -1,4 +1,4 @@ apiVersion: v1 kind: ConfigMap metadata: - namespace: openshift-config-managed + namespace: openshift-console diff --git a/pkg/console/assets/bindata.go b/pkg/console/assets/bindata.go index afe47c5490..41355b4c85 100644 --- a/pkg/console/assets/bindata.go +++ b/pkg/console/assets/bindata.go @@ -71,7 +71,7 @@ func (fi bindataFileInfo) Sys() interface{} { var _configmapsConsoleManagedClusterIngressCertConfigmapYaml = []byte(`apiVersion: v1 kind: ConfigMap metadata: - namespace: openshift-config-managed + namespace: openshift-console `) func configmapsConsoleManagedClusterIngressCertConfigmapYamlBytes() ([]byte, error) { diff --git a/pkg/console/controllers/managedclusters/controller.go b/pkg/console/controllers/managedclusters/controller.go index c94ee800fb..afa4c23b75 100644 --- a/pkg/console/controllers/managedclusters/controller.go +++ b/pkg/console/controllers/managedclusters/controller.go @@ -2,9 +2,7 @@ package managedcluster import ( "context" - "errors" "fmt" - "strings" // k8s apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -32,8 +30,7 @@ import ( //subresources configmapsub "github.com/openshift/console-operator/pkg/console/subresource/configmap" - managedclusteractionsub "github.com/openshift/console-operator/pkg/console/subresource/managedclusteraction" - managedclusterviewsub "github.com/openshift/console-operator/pkg/console/subresource/managedclusterview" + managedclustersub "github.com/openshift/console-operator/pkg/console/subresource/managedcluster" oauthsub "github.com/openshift/console-operator/pkg/console/subresource/oauthclient" secretsub "github.com/openshift/console-operator/pkg/console/subresource/secret" @@ -97,9 +94,9 @@ func NewManagedClusterController( util.ExcludeNamesFilter(api.HubClusterName), managedClusterInformers.Informer(), ).WithInformers( - dynamicInformers.ForResource(managedclusteractionsub.GetGroupVersionResource()).Informer(), + dynamicInformers.ForResource(managedclustersub.GetActionGroupVersionResource()).Informer(), ).WithInformers( - dynamicInformers.ForResource(managedclusterviewsub.GetGroupVersionResource()).Informer(), + dynamicInformers.ForResource(managedclustersub.GetViewGroupVersionResource()).Informer(), ).WithSync(ctrl.Sync). ToController("ManagedClusterController", recorder.WithComponentSuffix("managed-cluster-controller")) } @@ -132,8 +129,8 @@ func (c *ManagedClusterController) Sync(ctx context.Context, controllerContext f } // Get a list of ManagedCluster resources, degraded if error is returned - managedClusterClientConfigs, managedClusterClientConfigValidationErr, managedClusterClientConfigValidationErrReason := c.ValidateManagedClusterClientConfigs(ctx, operatorConfig, controllerContext.Recorder(), managedClusters) - statusHandler.AddConditions(status.HandleProgressingOrDegraded("ManagedClusterValidation", managedClusterClientConfigValidationErrReason, managedClusterClientConfigValidationErr)) + managedClusterClientConfigs, managedClusterClientConfigValidationErrReason, managedClusterClientConfigValidationErr := c.ValidateManagedClusterClientConfigs(ctx, operatorConfig, controllerContext.Recorder(), managedClusters) + statusHandler.AddConditions(status.HandleProgressingOrDegraded("ManagedClusterSync", managedClusterClientConfigValidationErrReason, managedClusterClientConfigValidationErr)) if managedClusterClientConfigValidationErr != nil { return statusHandler.FlushAndReturn(managedClusterClientConfigValidationErr) } @@ -144,36 +141,39 @@ func (c *ManagedClusterController) Sync(ctx context.Context, controllerContext f } // Create config maps for each managed cluster API server ca bundle - apiServerCASyncErr, apiServerCASyncErrReason := c.SyncAPIServerCAConfigMaps(managedClusterClientConfigs, ctx, operatorConfig, controllerContext.Recorder()) - statusHandler.AddConditions(status.HandleProgressingOrDegraded("ManagedClusterAPIServerCASync", apiServerCASyncErrReason, apiServerCASyncErr)) + apiServerCASyncErrReason, apiServerCASyncErr := c.SyncAPIServerCAConfigMaps(managedClusterClientConfigs, ctx, operatorConfig, controllerContext.Recorder()) + statusHandler.AddConditions(status.HandleProgressingOrDegraded("ManagedClusterSync", apiServerCASyncErrReason, apiServerCASyncErr)) if apiServerCASyncErr != nil { return statusHandler.FlushAndReturn(apiServerCASyncErr) } // Create managed cluster views for oauth clients - managedClusterViewOAuthClients, managedClusterViewOAuthClientErr, managedClusterViewOAuthClientErrReason := c.SyncManagedClusterViewOAuthClient(ctx, operatorConfig, managedClusters) - statusHandler.AddConditions(status.HandleProgressingOrDegraded("ManagedClusterViewOAuthClientSync", managedClusterViewOAuthClientErrReason, managedClusterViewOAuthClientErr)) + managedClusterViewOAuthClients, managedClusterViewOAuthClientErrReason, managedClusterViewOAuthClientErr := c.SyncManagedClusterViewOAuthClient(ctx, operatorConfig, managedClusters) + statusHandler.AddConditions(status.HandleProgressingOrDegraded("ManagedClusterSync", managedClusterViewOAuthClientErrReason, managedClusterViewOAuthClientErr)) + if managedClusterViewOAuthClientErr != nil { + return statusHandler.FlushAndReturn(managedClusterViewOAuthClientErr) + } // Create managed cluster actions for oauth clients - _, managedClusterActionCreateOAuthClientErr, managedClusterActionCreateOAuthClientErrReason := c.SyncManagedClusterActionCreateOAuthClient(ctx, operatorConfig, managedClusterViewOAuthClients) - statusHandler.AddConditions(status.HandleProgressingOrDegraded("ManagedClusterActionCreateOAuthClientSync", managedClusterActionCreateOAuthClientErrReason, managedClusterActionCreateOAuthClientErr)) + _, managedClusterActionCreateOAuthClientErrReason, managedClusterActionCreateOAuthClientErr := c.SyncManagedClusterActionCreateOAuthClient(ctx, operatorConfig, managedClusterViewOAuthClients) + statusHandler.AddConditions(status.HandleProgressingOrDegraded("ManagedClusterSync", managedClusterActionCreateOAuthClientErrReason, managedClusterActionCreateOAuthClientErr)) // Create managed cluster views for ingress cert - managedClusterViewsIngressCert, managedClusterViewIngressCertErr, managedClusterViewIngressCertErrReason := c.SyncManagedClusterViewIngressCert(ctx, operatorConfig, managedClusters) - statusHandler.AddConditions(status.HandleProgressingOrDegraded("ManagedClusterViewIngressCertSync", managedClusterViewIngressCertErrReason, managedClusterViewIngressCertErr)) + managedClusterViewsIngressCert, managedClusterViewIngressCertErrReason, managedClusterViewIngressCertErr := c.SyncManagedClusterViewIngressCert(ctx, operatorConfig, managedClusters) + statusHandler.AddConditions(status.HandleProgressingOrDegraded("ManagedClusterSync", managedClusterViewIngressCertErrReason, managedClusterViewIngressCertErr)) // Create config maps for each managed cluster ingress cert bundle - managedClusterIngressCertSyncErr, managedClusterIngressCertSyncErrReason := c.SyncManagedClusterIngressCertConfigMap(managedClusterViewsIngressCert, ctx, operatorConfig, controllerContext.Recorder()) - statusHandler.AddConditions(status.HandleProgressingOrDegraded("ManagedClusterIngressCertConfigMapSync", managedClusterIngressCertSyncErrReason, managedClusterIngressCertSyncErr)) + managedClusterIngressCertSyncErrReason, managedClusterIngressCertSyncErr := c.SyncManagedClusterIngressCertConfigMap(managedClusterViewsIngressCert, ctx, operatorConfig, controllerContext.Recorder()) + statusHandler.AddConditions(status.HandleProgressingOrDegraded("ManagedClusterSync", managedClusterIngressCertSyncErrReason, managedClusterIngressCertSyncErr)) // Create manged cluster config map - configSyncErr, configSyncErrReason := c.SyncManagedClusterConfigMap(managedClusterClientConfigs, ctx, operatorConfig, controllerContext.Recorder()) - statusHandler.AddConditions(status.HandleProgressingOrDegraded("ManagedClusterConfigSync", configSyncErrReason, configSyncErr)) + configSyncErrReason, configSyncErr := c.SyncManagedClusterConfigMap(managedClusterClientConfigs, ctx, operatorConfig, controllerContext.Recorder()) + statusHandler.AddConditions(status.HandleProgressingOrDegraded("ManagedClusterSync", configSyncErrReason, configSyncErr)) return statusHandler.FlushAndReturn(configSyncErr) } -// Return slice of clusterv1.ClientConfigs that have been validated or error and reaons if unable to list ManagedClusters -func (c *ManagedClusterController) ValidateManagedClusterClientConfigs(ctx context.Context, operatorConfig *operatorv1.Console, recorder events.Recorder, managedClusters *clusterv1.ManagedClusterList) (map[string]*clusterv1.ClientConfig, error, string) { +// Return slice of clusterv1.ClientConfigs that have been validated or error and its reason, if unable to list ManagedClusters +func (c *ManagedClusterController) ValidateManagedClusterClientConfigs(ctx context.Context, operatorConfig *operatorv1.Console, recorder events.Recorder, managedClusters *clusterv1.ManagedClusterList) (map[string]*clusterv1.ClientConfig, string, error) { validatedClientConfigs := map[string]*clusterv1.ClientConfig{} for _, managedCluster := range managedClusters.Items { clusterName := managedCluster.GetName() @@ -200,52 +200,52 @@ func (c *ManagedClusterController) ValidateManagedClusterClientConfigs(ctx conte validatedClientConfigs[clusterName] = &clientConfigs[0] } - return validatedClientConfigs, nil, "" + return validatedClientConfigs, "", nil } // Using ManagedCluster.spec.ManagedClusterClientConfigs, sync ConfigMaps containing the API server CA bundle for each managed cluster // If a managed cluster doesn't have complete client config yet, the information is logged, but no error is returned // If applying any ConfigMap fails, an error and reason are returned -func (c *ManagedClusterController) SyncAPIServerCAConfigMaps(clientConfigs map[string]*clusterv1.ClientConfig, ctx context.Context, operatorConfig *operatorv1.Console, recorder events.Recorder) (error, string) { - errs := []string{} +func (c *ManagedClusterController) SyncAPIServerCAConfigMaps(clientConfigs map[string]*clusterv1.ClientConfig, ctx context.Context, operatorConfig *operatorv1.Console, recorder events.Recorder) (string, error) { + errs := []error{} for clusterName, clientConfig := range clientConfigs { // Apply the config map. If this fails for any managed cluster, operator is degraded required := configmapsub.DefaultAPIServerCAConfigMap(clusterName, clientConfig.CABundle, operatorConfig) _, _, configMapApplyError := resourceapply.ApplyConfigMap(c.configMapClient, recorder, required) if configMapApplyError != nil { - klog.V(4).Infoln(fmt.Sprintf("Skipping API server CA ConfigMap sync for managed cluster %v, Error applying ConfigMap", clusterName)) - errs = append(errs, configMapApplyError.Error()) + klog.V(4).Infoln(fmt.Sprintf("error applying %s ConfigMap: %v. Skipping API server CA ConfigMap sync for managed cluster %s", required.GetName(), configMapApplyError, clusterName)) + errs = append(errs, configMapApplyError) continue } } // Return any apply errors that occurred if len(errs) > 0 { - return errors.New(strings.Join(errs, "\n")), "APIServerCAConfigMapSyncError" + return "APIServerCAConfigMapSyncError", fmt.Errorf("one or more errors during API server CA ConfigMap sync: %v", errs) } // Success - return nil, "" + return "", nil } // Using ManagedClusters.Spec.ManagedClusterClientConfigs and previously synced CA bundles, sync a ConfigMap containing serverconfig.ManagedClusterConfig YAML for each managed cluster // If a managed cluster doesn't have an API server CA bundle ConfigMap yet or the client config is incomplete, this is logged, but no error is returned // If applying the ConfigMap fails, an error and reason are returned -func (c *ManagedClusterController) SyncManagedClusterConfigMap(clientConfigs map[string]*clusterv1.ClientConfig, ctx context.Context, operatorConfig *operatorv1.Console, recorder events.Recorder) (error, string) { +func (c *ManagedClusterController) SyncManagedClusterConfigMap(clientConfigs map[string]*clusterv1.ClientConfig, ctx context.Context, operatorConfig *operatorv1.Console, recorder events.Recorder) (string, error) { managedClusterConfigs := []consoleserver.ManagedClusterConfig{} for clusterName, clientConfig := range clientConfigs { - klog.V(4).Infoln(fmt.Sprintf("Building config for managed cluster: %v", clusterName)) + klog.V(4).Infoln(fmt.Sprintf("Building config for managed cluster: %s", clusterName)) // Check that managed cluster CA ConfigMap has already been synced, skip if not found _, err := c.configMapClient.ConfigMaps(api.OpenShiftConsoleNamespace).Get(ctx, configmapsub.APIServerCAConfigMapName(clusterName), metav1.GetOptions{}) if apierrors.IsNotFound(err) { - klog.V(4).Infof("CA file not found for managed cluster %v", clusterName) + klog.V(4).Infof("CA file not found for managed cluster %s", clusterName) continue } // Skip if unable to get managed cluster API server config map for any other reason if err != nil { - klog.V(4).Infof("Error getting CA file for managed cluster %v", clusterName) + klog.V(4).Infof("Error getting CA file for managed cluster %s", clusterName) continue } @@ -261,151 +261,152 @@ func (c *ManagedClusterController) SyncManagedClusterConfigMap(clientConfigs map if len(managedClusterConfigs) > 0 { required, err := configmapsub.DefaultManagedClustersConfigMap(operatorConfig, managedClusterConfigs) if err != nil { - return err, "FailedMarshallingYAML" + return "FailedMarshallingYAML", err } if _, _, applyErr := resourceapply.ApplyConfigMap(c.configMapClient, recorder, required); applyErr != nil { - return applyErr, "FailedApply" + return "FailedApply", applyErr } } - return nil, "" + return "", nil } -func (c *ManagedClusterController) SyncManagedClusterViewOAuthClient(ctx context.Context, operatorConfig *operatorv1.Console, managedClusters *clusterv1.ManagedClusterList) ([]*unstructured.Unstructured, error, string) { - errs := []string{} +func (c *ManagedClusterController) SyncManagedClusterViewOAuthClient(ctx context.Context, operatorConfig *operatorv1.Console, managedClusters *clusterv1.ManagedClusterList) ([]*unstructured.Unstructured, string, error) { + errs := []error{} managedClusterOAuthClientViews := []*unstructured.Unstructured{} for _, managedCluster := range managedClusters.Items { - mcvOAuth := managedclusterviewsub.DefaultViewOAuthClient(operatorConfig, managedCluster.Name) - gvr := managedclusterviewsub.GetGroupVersionResource() + mcvOAuth := managedclustersub.DefaultViewOAuthClient(operatorConfig, managedCluster.Name) - oAuthResp, oAuthErr := c.dynamicClient.Resource(gvr).Namespace(managedCluster.Name).Create(ctx, mcvOAuth, metav1.CreateOptions{}) - if oAuthErr != nil && apierrors.IsAlreadyExists(oAuthErr) { - mcvOAuthName, _ := managedclusterviewsub.GetName(mcvOAuth) - oAuthResp, oAuthErr = c.dynamicClient.Resource(gvr).Namespace(managedCluster.Name).Get(ctx, mcvOAuthName, metav1.GetOptions{}) + oAuthResp, oAuthErr := c.dynamicClient.Resource(managedclustersub.GetViewGroupVersionResource()).Namespace(managedCluster.Name).Create(ctx, mcvOAuth, metav1.CreateOptions{}) + if apierrors.IsAlreadyExists(oAuthErr) { + mcvOAuthName, _ := managedclustersub.GetName(mcvOAuth) + oAuthResp, oAuthErr = c.dynamicClient.Resource(managedclustersub.GetViewGroupVersionResource()).Namespace(managedCluster.Name).Get(ctx, mcvOAuthName, metav1.GetOptions{}) } if oAuthErr != nil || oAuthResp == nil { - errs = append(errs, fmt.Sprintf("Error syncing managed cluster view for oauth client for cluster %s: %v", managedCluster.Name, oAuthErr)) + errs = append(errs, fmt.Errorf("error syncing ManagedClusterView for oauth client for cluster %s: %v", managedCluster.Name, oAuthErr)) } else { managedClusterOAuthClientViews = append(managedClusterOAuthClientViews, oAuthResp) } } if len(errs) > 0 { - return nil, errors.New(strings.Join(errs, "\n")), "ManagedClusterViewOAuthClientSyncError" + return nil, "ManagedClusterViewOAuthClientSyncError", fmt.Errorf("one or more errors during ManagedClusterView oauth client sync: %v", errs) } - return managedClusterOAuthClientViews, nil, "" + return managedClusterOAuthClientViews, "", nil } -func (c *ManagedClusterController) SyncManagedClusterActionCreateOAuthClient(ctx context.Context, operatorConfig *operatorv1.Console, managedClusterOAuthClientViews []*unstructured.Unstructured) ([]*unstructured.Unstructured, error, string) { +func (c *ManagedClusterController) SyncManagedClusterActionCreateOAuthClient(ctx context.Context, operatorConfig *operatorv1.Console, managedClusterOAuthClientViews []*unstructured.Unstructured) ([]*unstructured.Unstructured, string, error) { managedClusterList := []string{} - managedClusterListErrors := []string{} + managedClusterListErrors := []error{} for _, managedClusterOAuthView := range managedClusterOAuthClientViews { - status, statusErr := managedclusterviewsub.GetStatus(managedClusterOAuthView) - if !status || statusErr != nil { - namespace, namespaceErr := managedclusterviewsub.GetNamespace(managedClusterOAuthView) - if namespaceErr != nil || namespace == "" { - managedClusterListErrors = append(managedClusterListErrors, fmt.Sprintf("Unable to create oauth client for cluster %v: managed cluster view status unknown", namespace)) - } else { - managedClusterList = append(managedClusterList, namespace) - } + status, _ := managedclustersub.GetStatus(managedClusterOAuthView) + namespace, namespaceErr := managedclustersub.GetNamespace(managedClusterOAuthView) + if status { + klog.V(4).Infof("Skipping creating oauth client action for managed cluster %s: already exists", namespace) + continue + } + if namespaceErr != nil || namespace == "" { + klog.V(4).Infof("Error retrieving namespace of managed cluster from ManagedClusterView: %v", namespaceErr) + managedClusterListErrors = append(managedClusterListErrors, fmt.Errorf("unable to create oauth client for cluster %s: ManagedClusterView status unknown", namespace)) + continue } + managedClusterList = append(managedClusterList, namespace) } secret, secErr := c.secretsClient.Secrets(api.TargetNamespace).Get(ctx, secretsub.Stub().Name, metav1.GetOptions{}) if secErr != nil { - return nil, fmt.Errorf("Failed to get secret: %v", secErr), "ManagedClusterActionCreateOAuthClientSyncError" + return nil, "ManagedClusterActionCreateOAuthClientSyncError", fmt.Errorf("failed to get secret: %v", secErr) } oauthClient, oAuthErr := c.oauthClient.OAuthClients().Get(ctx, oauthsub.Stub().Name, metav1.GetOptions{}) if oAuthErr != nil { - return nil, fmt.Errorf("Failed to get oauth client: %v", oAuthErr), "ManagedClusterActionCreateOAuthClientSyncError" + return nil, "ManagedClusterActionCreateOAuthClientSyncError", fmt.Errorf("failed to get oauth client: %v", oAuthErr) } - errs := []string{} + errs := []error{} managedClusterActionCreateOAuthClients := []*unstructured.Unstructured{} secretString := secretsub.GetSecretString(secret) redirects := oauthsub.GetRedirectURIs(oauthClient) for _, managedClusterName := range managedClusterList { - mca := managedclusteractionsub.DefaultCreateOAuthClient(operatorConfig, managedClusterName, secretString, redirects) - gvr := managedclusteractionsub.GetGroupVersionResource() - opt := metav1.CreateOptions{} - oAuthCreateResp, oAuthCreateErr := c.dynamicClient.Resource(gvr).Namespace(managedClusterName).Create(ctx, mca, opt) + mca := managedclustersub.DefaultCreateOAuthClient(operatorConfig, managedClusterName, secretString, redirects) + gvr := managedclustersub.GetActionGroupVersionResource() + oAuthCreateResp, oAuthCreateErr := c.dynamicClient.Resource(gvr).Namespace(managedClusterName).Create(ctx, mca, metav1.CreateOptions{}) if oAuthCreateErr != nil && apierrors.IsAlreadyExists(oAuthCreateErr) { - mcaOAuthName, _ := managedclusteractionsub.GetName(mca) + mcaOAuthName, _ := managedclustersub.GetName(mca) oAuthCreateResp, oAuthCreateErr = c.dynamicClient.Resource(gvr).Namespace(managedClusterName).Get(ctx, mcaOAuthName, metav1.GetOptions{}) } if oAuthCreateErr != nil { - errs = append(errs, fmt.Sprintf("Error syncing managed cluster action for oauth client for cluster %s: %v", managedClusterName, oAuthCreateErr)) + errs = append(errs, fmt.Errorf("error syncing ManagedClusterAction for oauth client for cluster %s: %v", managedClusterName, oAuthCreateErr)) } else { managedClusterActionCreateOAuthClients = append(managedClusterActionCreateOAuthClients, oAuthCreateResp) } } if len(errs) > 0 { - return nil, errors.New(strings.Join(errs, "\n")), "ManagedClusterActionCreateOAuthClientSyncError" + return nil, "ManagedClusterActionCreateOAuthClientSyncError", fmt.Errorf("one or more errors during ManagedClusterAction create oauth client sync: %v", errs) } if len(managedClusterListErrors) > 0 { - return nil, errors.New(strings.Join(managedClusterListErrors, "\n")), "ManagedClusterActionCreateOAuthClientSyncError" + return nil, "ManagedClusterActionCreateOAuthClientSyncError", fmt.Errorf("one or more errors listing managed clusters from ManagedClusterViews: %v", managedClusterListErrors) } - return managedClusterActionCreateOAuthClients, nil, "" + return managedClusterActionCreateOAuthClients, "", nil } -func (c *ManagedClusterController) SyncManagedClusterViewIngressCert(ctx context.Context, operatorConfig *operatorv1.Console, managedClusters *clusterv1.ManagedClusterList) ([]*unstructured.Unstructured, error, string) { - errs := []string{} +func (c *ManagedClusterController) SyncManagedClusterViewIngressCert(ctx context.Context, operatorConfig *operatorv1.Console, managedClusters *clusterv1.ManagedClusterList) ([]*unstructured.Unstructured, string, error) { + errs := []error{} managedClusterIngressCertViews := []*unstructured.Unstructured{} for _, managedCluster := range managedClusters.Items { - mcvIngress := managedclusterviewsub.DefaultViewIngressCert(operatorConfig, managedCluster.Name) - gvr := managedclusterviewsub.GetGroupVersionResource() + mcvIngress := managedclustersub.DefaultViewIngressCert(operatorConfig, managedCluster.Name) - ingressResp, ingressErr := c.dynamicClient.Resource(gvr).Namespace(managedCluster.Name).Create(ctx, mcvIngress, metav1.CreateOptions{}) + ingressResp, ingressErr := c.dynamicClient.Resource(managedclustersub.GetViewGroupVersionResource()).Namespace(managedCluster.Name).Create(ctx, mcvIngress, metav1.CreateOptions{}) if ingressErr != nil && apierrors.IsAlreadyExists(ingressErr) { - mcvIngressName, _ := managedclusterviewsub.GetName(mcvIngress) - ingressResp, ingressErr = c.dynamicClient.Resource(gvr).Namespace(managedCluster.Name).Get(ctx, mcvIngressName, metav1.GetOptions{}) + mcvIngressName, _ := managedclustersub.GetName(mcvIngress) + ingressResp, ingressErr = c.dynamicClient.Resource(managedclustersub.GetViewGroupVersionResource()).Namespace(managedCluster.Name).Get(ctx, mcvIngressName, metav1.GetOptions{}) } - if ingressErr != nil || ingressResp == nil { - errs = append(errs, fmt.Sprintf("Error syncing managed cluster view ingress cert for cluster %s: %v", managedCluster.Name, ingressErr)) - } else { - managedClusterIngressCertViews = append(managedClusterIngressCertViews, ingressResp) + if ingressErr != nil { + errs = append(errs, fmt.Errorf("error syncing ManagedClusterView ingress cert for cluster %s: %v", managedCluster.Name, ingressErr)) + continue } + managedClusterIngressCertViews = append(managedClusterIngressCertViews, ingressResp) } if len(errs) > 0 { - return nil, errors.New(strings.Join(errs, "\n")), "ManagedClusterViewIngressCertSyncError" + return nil, "ManagedClusterViewIngressCertSyncError", fmt.Errorf("one or more errors during ManagedClusterView ingress cert sync: %v", errs) } - return managedClusterIngressCertViews, nil, "" + return managedClusterIngressCertViews, "", nil } -func (c *ManagedClusterController) SyncManagedClusterIngressCertConfigMap(managedClusterIngressCertViews []*unstructured.Unstructured, ctx context.Context, operatorConfig *operatorv1.Console, recorder events.Recorder) (error, string) { - errs := []string{} +func (c *ManagedClusterController) SyncManagedClusterIngressCertConfigMap(managedClusterIngressCertViews []*unstructured.Unstructured, ctx context.Context, operatorConfig *operatorv1.Console, recorder events.Recorder) (string, error) { + errs := []error{} for _, mcvIngress := range managedClusterIngressCertViews { - clusterName, _ := managedclusterviewsub.GetNamespace(mcvIngress) - certBundle, _ := managedclusterviewsub.GetCertBundle(mcvIngress) + clusterName, _ := managedclustersub.GetNamespace(mcvIngress) + certBundle, _ := managedclustersub.GetCertBundle(mcvIngress) required := configmapsub.DefaultManagedClusterIngressCertConfigMap(clusterName, certBundle, operatorConfig) _, _, configMapApplyError := resourceapply.ApplyConfigMap(c.configMapClient, recorder, required) if configMapApplyError != nil { klog.V(4).Infoln(fmt.Sprintf("Skipping Ingress certificate ConfigMap sync for managed cluster %v, Error applying ConfigMap", clusterName)) - errs = append(errs, configMapApplyError.Error()) + errs = append(errs, configMapApplyError) continue } } if len(errs) > 0 { - return errors.New(strings.Join(errs, "\n")), "ManagedClusterIngressCertConfigMapSyncError" + return "ManagedClusterIngressCertConfigMapSyncError", fmt.Errorf("one or more errors during managed cluster ingress cert configmap sync: %v", errs) } - return nil, "" + return "", nil } +// TODO use an object in the controller struct or a label to track resources to be removed func (c *ManagedClusterController) removeManagedClusters(ctx context.Context) error { - errs := []string{} + errs := []error{} configMaps, err := c.configMapClient.ConfigMaps(api.OpenShiftConsoleNamespace).List(ctx, metav1.ListOptions{LabelSelector: api.ManagedClusterLabel}) if err != nil { klog.Errorf("Error listing managed cluster resources to remove: %v", err) @@ -420,11 +421,11 @@ func (c *ManagedClusterController) removeManagedClusters(ctx context.Context) er for _, configMap := range configMaps.Items { deletionErr := c.configMapClient.ConfigMaps(api.OpenShiftConsoleNamespace).Delete(ctx, configMap.GetName(), metav1.DeleteOptions{}) if deletionErr != nil && !apierrors.IsNotFound(deletionErr) { - errs = append(errs, deletionErr.Error()) + errs = append(errs, deletionErr) } } if len(errs) > 0 { - return errors.New(strings.Join(errs, "\n")) + return fmt.Errorf("one or more errors during removal of managed clusters: %v", errs) } return nil } diff --git a/pkg/console/operator/sync_v400.go b/pkg/console/operator/sync_v400.go index dd8bc6a3ab..cba87f5cdb 100644 --- a/pkg/console/operator/sync_v400.go +++ b/pkg/console/operator/sync_v400.go @@ -71,17 +71,17 @@ func (co *consoleOperator) sync_v400(ctx context.Context, controllerContext fact return statusHandler.FlushAndReturn(routeErr) } - cm, cmChanged, cmErrReason, cmErr := co.SyncConfigMap(ctx, set.Operator, set.Console, set.Infrastructure, set.OAuth, route, controllerContext.Recorder()) + // managed-clusters ConfigMap is managed by another controller and is not required, we don't need to exit the sync loop if it's not present + canMountManagedClusterConfig, managedClusterConfigErrReason, managedClusterConfigErr := co.SyncManagedClusterConfigMap(ctx) + statusHandler.AddConditions(status.HandleProgressingOrDegraded("ManagedClusterConfigSync", managedClusterConfigErrReason, managedClusterConfigErr)) + + cm, cmChanged, cmErrReason, cmErr := co.SyncConfigMap(ctx, set.Operator, set.Console, set.Infrastructure, set.OAuth, route, canMountManagedClusterConfig, controllerContext.Recorder()) toUpdate = toUpdate || cmChanged statusHandler.AddConditions(status.HandleProgressingOrDegraded("ConfigMapSync", cmErrReason, cmErr)) if cmErr != nil { return statusHandler.FlushAndReturn(cmErr) } - // managed-clusters ConfigMap is managed by another controller and is not required, we don't need to exit the sync loop if it's not present - canMountManagedClusterConfig, managedClusterConfigErrReason, managedClusterConfigErr := co.SyncManagedClusterConfigMap(ctx) - statusHandler.AddConditions(status.HandleProgressingOrDegraded("ManagedClusterConfigSync", managedClusterConfigErrReason, managedClusterConfigErr)) - serviceCAConfigMap, serviceCAChanged, serviceCAErrReason, serviceCAErr := co.SyncServiceCAConfigMap(ctx, set.Operator) toUpdate = toUpdate || serviceCAChanged statusHandler.AddConditions(status.HandleProgressingOrDegraded("ServiceCASync", serviceCAErrReason, serviceCAErr)) @@ -317,6 +317,7 @@ func (co *consoleOperator) SyncConfigMap( infrastructureConfig *configv1.Infrastructure, oauthConfig *configv1.OAuth, activeConsoleRoute *routev1.Route, + canMountManagedClusterConfig bool, recorder events.Recorder, ) (consoleConfigMap *corev1.ConfigMap, changed bool, reason string, err error) { @@ -349,7 +350,7 @@ func (co *consoleOperator) SyncConfigMap( } pluginsEndpointMap := co.GetPluginsEndpointMap(operatorConfig.Spec.Plugins) - defaultConfigmap, _, err := configmapsub.DefaultConfigMap(operatorConfig, consoleConfig, managedConfig, infrastructureConfig, activeConsoleRoute, useDefaultCAFile, inactivityTimeoutSeconds, pluginsEndpointMap) + defaultConfigmap, _, err := configmapsub.DefaultConfigMap(operatorConfig, consoleConfig, managedConfig, infrastructureConfig, activeConsoleRoute, useDefaultCAFile, inactivityTimeoutSeconds, pluginsEndpointMap, canMountManagedClusterConfig) if err != nil { return nil, false, "FailedConsoleConfigBuilder", err } @@ -380,7 +381,7 @@ func (co *consoleOperator) SyncManagedClusterConfigMap(ctx context.Context) (boo // Degraded if managed cluster config map is present but doesn't have the correct data key _, ok := managedClusterConfigMap.Data[api.ManagedClusterConfigKey] if !ok { - return false, "MissingManagedClusterConfig", fmt.Errorf("%v ConfigMap is missing %v data key", api.ManagedClusterConfigMapName, api.ManagedClusterConfigKey) + return false, "MissingManagedClusterConfig", fmt.Errorf("%s ConfigMap is missing %s data key", api.ManagedClusterConfigMapName, api.ManagedClusterConfigKey) } // Managed cluster config map is present and can be mounted diff --git a/pkg/console/subresource/configmap/configmap.go b/pkg/console/subresource/configmap/configmap.go index da31d2799a..ca2c695d38 100644 --- a/pkg/console/subresource/configmap/configmap.go +++ b/pkg/console/subresource/configmap/configmap.go @@ -43,7 +43,13 @@ func DefaultConfigMap( activeConsoleRoute *routev1.Route, useDefaultCAFile bool, inactivityTimeoutSeconds int, - pluginsEndpoingMap map[string]string) (consoleConfigmap *corev1.ConfigMap, unsupportedOverridesHaveMerged bool, err error) { + pluginsEndpoingMap map[string]string, + canMountManagedClusterConfig bool) (consoleConfigmap *corev1.ConfigMap, unsupportedOverridesHaveMerged bool, err error) { + + managedClusterConfigFile := "" + if canMountManagedClusterConfig { + managedClusterConfigFile = fmt.Sprintf("%s/%s", api.ManagedClusterConfigMountDir, api.ManagedClusterConfigKey) + } defaultBuilder := &consoleserver.ConsoleServerCLIConfigBuilder{} defaultConfig, err := defaultBuilder.Host(activeConsoleRoute.Spec.Host). @@ -53,7 +59,7 @@ func DefaultConfigMap( OAuthServingCert(useDefaultCAFile). APIServerURL(getApiUrl(infrastructureConfig)). InactivityTimeout(inactivityTimeoutSeconds). - ManagedClusterConfigFile(fmt.Sprintf("%v/%v", api.ManagedClusterConfigMountDir, api.ManagedClusterConfigKey)). + ManagedClusterConfigFile(managedClusterConfigFile). ConfigYAML() if err != nil { klog.Errorf("failed to generate default console-config config: %v", err) diff --git a/pkg/console/subresource/configmap/configmap_test.go b/pkg/console/subresource/configmap/configmap_test.go index 18fe9dad3f..55443657fc 100644 --- a/pkg/console/subresource/configmap/configmap_test.go +++ b/pkg/console/subresource/configmap/configmap_test.go @@ -30,14 +30,15 @@ const ( // To manually run these tests: go test -v ./pkg/console/subresource/configmap/... func TestDefaultConfigMap(t *testing.T) { type args struct { - operatorConfig *operatorv1.Console - consoleConfig *configv1.Console - managedConfig *corev1.ConfigMap - infrastructureConfig *configv1.Infrastructure - rt *routev1.Route - useDefaultCAFile bool - inactivityTimeoutSeconds int - enabledPlugins map[string]string + operatorConfig *operatorv1.Console + consoleConfig *configv1.Console + managedConfig *corev1.ConfigMap + infrastructureConfig *configv1.Infrastructure + rt *routev1.Route + useDefaultCAFile bool + inactivityTimeoutSeconds int + enabledPlugins map[string]string + canMountManagedClusterConfig bool } tests := []struct { name string @@ -63,8 +64,9 @@ func TestDefaultConfigMap(t *testing.T) { Host: host, }, }, - useDefaultCAFile: true, - inactivityTimeoutSeconds: 0, + useDefaultCAFile: true, + inactivityTimeoutSeconds: 0, + canMountManagedClusterConfig: true, }, want: &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ @@ -114,8 +116,9 @@ providers: {} Host: host, }, }, - useDefaultCAFile: false, - inactivityTimeoutSeconds: 0, + useDefaultCAFile: false, + inactivityTimeoutSeconds: 0, + canMountManagedClusterConfig: true, }, want: &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ @@ -174,8 +177,9 @@ customization: Host: host, }, }, - useDefaultCAFile: true, - inactivityTimeoutSeconds: 0, + useDefaultCAFile: true, + inactivityTimeoutSeconds: 0, + canMountManagedClusterConfig: true, }, want: &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ @@ -243,8 +247,9 @@ customization: Host: host, }, }, - useDefaultCAFile: true, - inactivityTimeoutSeconds: 0, + useDefaultCAFile: true, + inactivityTimeoutSeconds: 0, + canMountManagedClusterConfig: true, }, want: &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ @@ -317,8 +322,9 @@ customization: Host: host, }, }, - useDefaultCAFile: true, - inactivityTimeoutSeconds: 0, + useDefaultCAFile: true, + inactivityTimeoutSeconds: 0, + canMountManagedClusterConfig: true, }, want: &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ @@ -393,8 +399,9 @@ customization: Host: host, }, }, - useDefaultCAFile: true, - inactivityTimeoutSeconds: 0, + useDefaultCAFile: true, + inactivityTimeoutSeconds: 0, + canMountManagedClusterConfig: true, }, want: &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ @@ -451,8 +458,9 @@ providers: Host: customHostname, }, }, - useDefaultCAFile: false, - inactivityTimeoutSeconds: 0, + useDefaultCAFile: false, + inactivityTimeoutSeconds: 0, + canMountManagedClusterConfig: true, }, want: &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ @@ -503,8 +511,9 @@ providers: {} Host: host, }, }, - useDefaultCAFile: true, - inactivityTimeoutSeconds: 60, + useDefaultCAFile: true, + inactivityTimeoutSeconds: 60, + canMountManagedClusterConfig: true, }, want: &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ @@ -561,6 +570,7 @@ providers: {} "plugin1": "plugin1_url", "plugin2": "plugin2_url", }, + canMountManagedClusterConfig: true, }, want: &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ @@ -590,6 +600,57 @@ providers: {} plugins: plugin1: plugin1_url plugin2: plugin2_url +`, + }, + }, + }, + { + name: "Test canMountManagedClusterConfig set to false", + args: args{ + operatorConfig: &operatorv1.Console{}, + consoleConfig: &configv1.Console{}, + managedConfig: &corev1.ConfigMap{}, + infrastructureConfig: &configv1.Infrastructure{ + Status: configv1.InfrastructureStatus{ + APIServerURL: mockAPIServer, + }, + }, + rt: &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: api.OpenShiftConsoleName, + }, + Spec: routev1.RouteSpec{ + Host: host, + }, + }, + useDefaultCAFile: true, + inactivityTimeoutSeconds: 0, + canMountManagedClusterConfig: false, + }, + want: &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: api.OpenShiftConsoleConfigMapName, + Namespace: api.OpenShiftConsoleNamespace, + Labels: map[string]string{"app": api.OpenShiftConsoleName}, + Annotations: map[string]string{}, + }, + Data: map[string]string{configKey: `kind: ConsoleConfig +apiVersion: console.openshift.io/v1 +auth: + clientID: console + clientSecretFile: /var/oauth-config/clientSecret + oauthEndpointCAFile: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt +clusterInfo: + consoleBaseAddress: https://` + host + ` + masterPublicURL: ` + mockAPIServer + ` +customization: + branding: ` + DEFAULT_BRAND + ` + documentationBaseURL: ` + DEFAULT_DOC_URL + ` +servingInfo: + bindAddress: https://[::]:8443 + certFile: /var/serving-cert/tls.crt + keyFile: /var/serving-cert/tls.key +providers: {} `, }, }, @@ -606,6 +667,7 @@ plugins: tt.args.useDefaultCAFile, tt.args.inactivityTimeoutSeconds, tt.args.enabledPlugins, + tt.args.canMountManagedClusterConfig, ) // marshall the exampleYaml to map[string]interface{} so we can use it in diff below diff --git a/pkg/console/subresource/configmap/managed_clusters_test.go b/pkg/console/subresource/configmap/managed_clusters_test.go index 9562f92f09..e52ac5db60 100644 --- a/pkg/console/subresource/configmap/managed_clusters_test.go +++ b/pkg/console/subresource/configmap/managed_clusters_test.go @@ -20,7 +20,7 @@ var ( URL: "test-url", CAFile: "/var/api/ca", }, - Oauth: consoleserver.ManagedClusterOAuthConfig{ + OAuth: consoleserver.ManagedClusterOAuthConfig{ ClientID: "test-client-id", ClientSecret: "test-client-secret", CAFile: "/var/oauth/ca", diff --git a/pkg/console/subresource/consoleserver/types.go b/pkg/console/subresource/consoleserver/types.go index 7db866fbf6..98c989cc36 100644 --- a/pkg/console/subresource/consoleserver/types.go +++ b/pkg/console/subresource/consoleserver/types.go @@ -143,5 +143,5 @@ type ManagedClusterOAuthConfig struct { type ManagedClusterConfig struct { Name string `json:"name" yaml:"name"` // ManagedCluster name, provided through ACM APIServer ManagedClusterAPIServerConfig `json:"apiServer" yaml:"apiServer"` - Oauth ManagedClusterOAuthConfig `json:"oauth" yaml:"oauth"` + OAuth ManagedClusterOAuthConfig `json:"oauth" yaml:"oauth"` } diff --git a/pkg/console/subresource/managedclusterview/default-ingress-cert.go b/pkg/console/subresource/managedcluster/default_ingress_cert.go similarity index 98% rename from pkg/console/subresource/managedclusterview/default-ingress-cert.go rename to pkg/console/subresource/managedcluster/default_ingress_cert.go index 9ee904c4da..dd7ed6de20 100644 --- a/pkg/console/subresource/managedclusterview/default-ingress-cert.go +++ b/pkg/console/subresource/managedcluster/default_ingress_cert.go @@ -1,4 +1,4 @@ -package managedclusterview +package managedcluster import ( operatorv1 "github.com/openshift/api/operator/v1" diff --git a/pkg/console/subresource/managedclusterview/oauth-client.go b/pkg/console/subresource/managedcluster/oauth_client.go similarity index 64% rename from pkg/console/subresource/managedclusterview/oauth-client.go rename to pkg/console/subresource/managedcluster/oauth_client.go index 879e0c31f8..55e4b7c1b4 100644 --- a/pkg/console/subresource/managedclusterview/oauth-client.go +++ b/pkg/console/subresource/managedcluster/oauth_client.go @@ -1,4 +1,4 @@ -package managedclusterview +package managedcluster import ( operatorv1 "github.com/openshift/api/operator/v1" @@ -6,13 +6,28 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" // openshift - "github.com/openshift/console-operator/pkg/console/assets" "github.com/openshift/console-operator/pkg/console/subresource/util" // acm - TODO conflicts adding package to go.mod with several dependencies // managedclusterviewv1beta1 "github.com/open-cluster-management/multicloud-operators-foundation/pkg/apis/action/v1beta1" ) +func DefaultCreateOAuthClient(cr *operatorv1.Console, cn string, sec string, redirects []string) *unstructured.Unstructured { + managedClusterAction := CreateOAuthClientStub(cn) + withDefaultCreateOAuthClientInfo(managedClusterAction, cn, sec, redirects) + return managedClusterAction +} + +func withDefaultCreateOAuthClientInfo(mca *unstructured.Unstructured, cn string, sec string, redirects []string) { + unstructured.SetNestedField(mca.Object, cn, "metadata", "namespace") + unstructured.SetNestedField(mca.Object, sec, "spec", "kube", "template", "secret") + unstructured.SetNestedStringSlice(mca.Object, redirects, "spec", "kube", "template", "redirectURIs") +} + +func CreateOAuthClientStub(cn string) *unstructured.Unstructured { + return util.ReadUnstructuredOrDie(assets.MustAsset("managedclusteractions/console-managed-cluster-action-create-oauth-client.yaml")) +} + func DefaultViewOAuthClient(cr *operatorv1.Console, cn string) *unstructured.Unstructured { managedClusterView := ViewOAuthClientStub(cn) withDefaultViewOAuthClientInfo(managedClusterView, cn) @@ -27,6 +42,22 @@ func ViewOAuthClientStub(cn string) *unstructured.Unstructured { return util.ReadUnstructuredOrDie(assets.MustAsset("managedclusterviews/console-managed-cluster-view-oauth-client.yaml")) } +func GetActionGroupVersionResource() schema.GroupVersionResource { + return schema.GroupVersionResource{ + Group: "view.open-cluster-management.io", + Version: "v1beta1", + Resource: "managedclusterviews", + } +} + +func GetViewGroupVersionResource() schema.GroupVersionResource { + return schema.GroupVersionResource{ + Group: "action.open-cluster-management.io", + Version: "v1beta1", + Resource: "managedclusteractions", + } +} + func GetStatus(mcv *unstructured.Unstructured) (bool, error) { conditions, found, err := unstructured.NestedSlice(mcv.Object, "status", "conditions") if err != nil || !found || len(conditions) == 0 { @@ -57,11 +88,3 @@ func GetNamespace(mcv *unstructured.Unstructured) (string, error) { } return namespace, nil } - -func GetGroupVersionResource() schema.GroupVersionResource { - return schema.GroupVersionResource{ - Group: "view.open-cluster-management.io", - Version: "v1beta1", - Resource: "managedclusterviews", - } -} diff --git a/pkg/console/subresource/managedclusteraction/oauth-client.go b/pkg/console/subresource/managedclusteraction/oauth-client.go deleted file mode 100644 index cdad03b843..0000000000 --- a/pkg/console/subresource/managedclusteraction/oauth-client.go +++ /dev/null @@ -1,45 +0,0 @@ -package managedclusteraction - -import ( - operatorv1 "github.com/openshift/api/operator/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime/schema" - - // openshift - "github.com/openshift/console-operator/pkg/console/assets" - "github.com/openshift/console-operator/pkg/console/subresource/util" - // acm - TODO conflicts adding package to go.mod with several dependencies - // managedclusterviewv1beta1 "github.com/open-cluster-management/multicloud-operators-foundation/pkg/apis/action/v1beta1" -) - -func DefaultCreateOAuthClient(cr *operatorv1.Console, cn string, sec string, redirects []string) *unstructured.Unstructured { - managedClusterAction := CreateOAuthClientStub(cn) - withDefaultCreateOAuthClientInfo(managedClusterAction, cn, sec, redirects) - return managedClusterAction -} - -func withDefaultCreateOAuthClientInfo(mca *unstructured.Unstructured, cn string, sec string, redirects []string) { - unstructured.SetNestedField(mca.Object, cn, "metadata", "namespace") - unstructured.SetNestedField(mca.Object, sec, "spec", "kube", "template", "secret") - unstructured.SetNestedStringSlice(mca.Object, redirects, "spec", "kube", "template", "redirectURIs") -} - -func CreateOAuthClientStub(cn string) *unstructured.Unstructured { - return util.ReadUnstructuredOrDie(assets.MustAsset("managedclusteractions/console-managed-cluster-action-create-oauth-client.yaml")) -} - -func GetName(mca *unstructured.Unstructured) (string, error) { - name, found, err := unstructured.NestedString(mca.Object, "metadata", "name") - if err != nil || !found || name == "" { - return "", err - } - return name, nil -} - -func GetGroupVersionResource() schema.GroupVersionResource { - return schema.GroupVersionResource{ - Group: "action.open-cluster-management.io", - Version: "v1beta1", - Resource: "managedclusteractions", - } -}