diff --git a/backend/controllers/bindableresourcesrequest/bindableresourcesrequest_controller.go b/backend/controllers/bindableresourcesrequest/bindableresourcesrequest_controller.go index e6e7f509b..f4f5ba881 100644 --- a/backend/controllers/bindableresourcesrequest/bindableresourcesrequest_controller.go +++ b/backend/controllers/bindableresourcesrequest/bindableresourcesrequest_controller.go @@ -250,8 +250,14 @@ func (r *reconciler) reconcile(ctx context.Context, clusterName string, cl clien } } + // Default the pretty name to the request name if not provided. + clusterPrettyName := req.Spec.ClusterIdentity.PrettyName + if clusterPrettyName == "" { + clusterPrettyName = req.Name + } + // Handle resources and get kubeconfig - result, err := r.kubeManager.HandleResources(ctx, req.Spec.Author, req.Spec.ClusterIdentity.Identity, clusterName) + result, err := r.kubeManager.HandleResources(ctx, req.Spec.Author, req.Spec.ClusterIdentity.Identity, clusterName, clusterPrettyName) if err != nil { meta.SetStatusCondition(&req.Status.Conditions, metav1.Condition{ Type: string(kubebindv1alpha2.BindableResourcesRequestConditionReady), diff --git a/backend/http/handler.go b/backend/http/handler.go index 09865df98..0842c4695 100644 --- a/backend/http/handler.go +++ b/backend/http/handler.go @@ -433,7 +433,12 @@ func (h *handler) handleBind(w http.ResponseWriter, r *http.Request) { consumerID = identity } - handleResult, err := h.kubeManager.HandleResources(r.Context(), state.Token.Subject, consumerID, params.ClusterID) + clusterPrettyName := bindRequest.Spec.ClusterIdentity.PrettyName + if clusterPrettyName == "" { + clusterPrettyName = bindRequest.Name + } + + handleResult, err := h.kubeManager.HandleResources(r.Context(), state.Token.Subject, consumerID, params.ClusterID, clusterPrettyName) if err != nil { logger.Error(err, "failed to handle resources") statusCode, code, details := mapErrorToCode(err) @@ -599,7 +604,7 @@ func (h *handler) handleApplyBinding(w http.ResponseWriter, r *http.Request) { } // Get the provider kubeconfig for this user's namespace - handleResult, err := h.kubeManager.HandleResources(r.Context(), state.Token.Subject, identity, params.ClusterID) + handleResult, err := h.kubeManager.HandleResources(r.Context(), state.Token.Subject, identity, params.ClusterID, req.BindingName) if err != nil { logger.Error(err, "failed to handle resources for apply-binding") statusCode, code, details := mapErrorToCode(err) diff --git a/backend/kubernetes/manager.go b/backend/kubernetes/manager.go index 9f4acb8fd..2d30c7be6 100644 --- a/backend/kubernetes/manager.go +++ b/backend/kubernetes/manager.go @@ -107,7 +107,7 @@ type HandleResourcesResult struct { func (m *Manager) HandleResources( ctx context.Context, - author, identity, cluster string, + author, identity, cluster, clusterPrettyName string, ) (*HandleResourcesResult, error) { logger := klog.FromContext(ctx).WithValues("identity", identity) ctx = klog.NewContext(ctx, logger) @@ -148,7 +148,7 @@ func (m *Manager) HandleResources( err = c.Get(ctx, types.NamespacedName{Namespace: ns, Name: kuberesources.ClusterBindingName}, &cb) switch { case errors.IsNotFound(err): - if err := kuberesources.CreateClusterBinding(ctx, c, ns, "kubeconfig", m.providerPrettyName); err != nil { + if err := kuberesources.CreateClusterBinding(ctx, c, ns, "kubeconfig", m.providerPrettyName, identity, author, clusterPrettyName); err != nil { return nil, err } case err != nil: diff --git a/backend/kubernetes/resources/cluster_binding.go b/backend/kubernetes/resources/cluster_binding.go index b95e95e03..99ea0569a 100644 --- a/backend/kubernetes/resources/cluster_binding.go +++ b/backend/kubernetes/resources/cluster_binding.go @@ -26,13 +26,25 @@ import ( kubebindv1alpha2 "github.com/kube-bind/kube-bind/sdk/apis/kubebind/v1alpha2" ) -func CreateClusterBinding(ctx context.Context, client client.Client, ns, secretName, providerPrettyName string) error { +func CreateClusterBinding(ctx context.Context, client client.Client, ns, secretName, providerPrettyName, identity, author, clusterPrettyName string) error { logger := klog.FromContext(ctx) + annotations := map[string]string{} + if identity != "" { + annotations[IdentityAnnotationKey] = identity + } + if author != "" { + annotations[AuthorAnnotationKey] = author + } + if clusterPrettyName != "" { + annotations[PrettyNameAnnotationKey] = clusterPrettyName + } + clusterBinding := &kubebindv1alpha2.ClusterBinding{ ObjectMeta: metav1.ObjectMeta{ - Name: ClusterBindingName, - Namespace: ns, + Name: ClusterBindingName, + Namespace: ns, + Annotations: annotations, }, Spec: kubebindv1alpha2.ClusterBindingSpec{ ProviderPrettyName: providerPrettyName, diff --git a/backend/kubernetes/resources/namespace.go b/backend/kubernetes/resources/namespace.go index 75efaf6ef..59b800088 100644 --- a/backend/kubernetes/resources/namespace.go +++ b/backend/kubernetes/resources/namespace.go @@ -33,6 +33,7 @@ import ( const ( IdentityAnnotationKey = "backend.kube-bind.io/identity" AuthorAnnotationKey = "backend.kube-bind.io/author" + PrettyNameAnnotationKey = "backend.kube-bind.io/cluster-pretty-name" legacyIdentityAnnotationKey = "example-backend.kube-bind.io/identity" ) diff --git a/backend/options/options.go b/backend/options/options.go index 32149ede5..3076dbf7f 100644 --- a/backend/options/options.go +++ b/backend/options/options.go @@ -203,13 +203,15 @@ func (options *Options) AddFlags(fs *pflag.FlagSet) { } func (options *Options) Complete() (*CompletedOptions, error) { - if !options.FrontendDisabled { - // Serve must complete first as OIDC may depend on it - // to reuse the listener. - if err := options.Serve.Complete(); err != nil { - return nil, err - } + // Serve must complete first as OIDC may depend on it + // to reuse the listener. The web server is always started + // (at minimum for the healthz endpoint), so the listener + // is required even when the frontend is disabled. + if err := options.Serve.Complete(); err != nil { + return nil, err + } + if !options.FrontendDisabled { if err := options.OIDC.Complete(options.Serve.Listener); err != nil { return nil, err } diff --git a/contrib/kcp/deploy/resources/apiexport-kube-bind.io.yaml b/contrib/kcp/deploy/resources/apiexport-kube-bind.io.yaml index 1426f3255..ab9e3d02b 100644 --- a/contrib/kcp/deploy/resources/apiexport-kube-bind.io.yaml +++ b/contrib/kcp/deploy/resources/apiexport-kube-bind.io.yaml @@ -86,7 +86,7 @@ spec: crd: {} - group: kube-bind.io name: bindableresourcesrequests - schema: v260225-974c2a97.bindableresourcesrequests.kube-bind.io + schema: v260507-3080410e.bindableresourcesrequests.kube-bind.io storage: crd: {} - group: kube-bind.io diff --git a/contrib/kcp/deploy/resources/apiresourceschema-bindableresourcesrequests.kube-bind.io.yaml b/contrib/kcp/deploy/resources/apiresourceschema-bindableresourcesrequests.kube-bind.io.yaml index 0d237b04d..dbd24b501 100644 --- a/contrib/kcp/deploy/resources/apiresourceschema-bindableresourcesrequests.kube-bind.io.yaml +++ b/contrib/kcp/deploy/resources/apiresourceschema-bindableresourcesrequests.kube-bind.io.yaml @@ -1,7 +1,7 @@ apiVersion: apis.kcp.io/v1alpha1 kind: APIResourceSchema metadata: - name: v260225-974c2a97.bindableresourcesrequests.kube-bind.io + name: v260507-3080410e.bindableresourcesrequests.kube-bind.io spec: group: kube-bind.io names: @@ -67,6 +67,11 @@ spec: identity: description: Identity is the unique identifier of the cluster. type: string + prettyName: + description: |- + PrettyName is a human-readable name for the cluster, used for display + purposes. If not provided, the controller defaults it to the request name. + type: string type: object kubeconfigSecretRef: description: |- diff --git a/deploy/charts/backend/crds/kube-bind.io_bindableresourcesrequests.yaml b/deploy/charts/backend/crds/kube-bind.io_bindableresourcesrequests.yaml index 8d01ca822..b628ca26e 100644 --- a/deploy/charts/backend/crds/kube-bind.io_bindableresourcesrequests.yaml +++ b/deploy/charts/backend/crds/kube-bind.io_bindableresourcesrequests.yaml @@ -71,6 +71,11 @@ spec: identity: description: Identity is the unique identifier of the cluster. type: string + prettyName: + description: |- + PrettyName is a human-readable name for the cluster, used for display + purposes. If not provided, the controller defaults it to the request name. + type: string type: object kubeconfigSecretRef: description: |- diff --git a/deploy/crd/kube-bind.io_bindableresourcesrequests.yaml b/deploy/crd/kube-bind.io_bindableresourcesrequests.yaml index 8d01ca822..b628ca26e 100644 --- a/deploy/crd/kube-bind.io_bindableresourcesrequests.yaml +++ b/deploy/crd/kube-bind.io_bindableresourcesrequests.yaml @@ -71,6 +71,11 @@ spec: identity: description: Identity is the unique identifier of the cluster. type: string + prettyName: + description: |- + PrettyName is a human-readable name for the cluster, used for display + purposes. If not provided, the controller defaults it to the request name. + type: string type: object kubeconfigSecretRef: description: |- diff --git a/sdk/apis/kubebind/v1alpha2/cluster_types.go b/sdk/apis/kubebind/v1alpha2/cluster_types.go index 7ebe06df3..a8f7b27fd 100644 --- a/sdk/apis/kubebind/v1alpha2/cluster_types.go +++ b/sdk/apis/kubebind/v1alpha2/cluster_types.go @@ -78,6 +78,11 @@ type ClusterStatus struct { type ClusterIdentity struct { // Identity is the unique identifier of the cluster. Identity string `json:"identity,omitempty"` + + // PrettyName is a human-readable name for the cluster, used for display + // purposes. If not provided, the controller defaults it to the request name. + // +optional + PrettyName string `json:"prettyName,omitempty"` } // ClusterList is the objects list that represents the Cluster.