allow to reference via zitadel too
All checks were successful
Build and Publish / build-release (push) Successful in 2m9s
All checks were successful
Build and Publish / build-release (push) Successful in 2m9s
This commit is contained in:
@@ -18,6 +18,7 @@ package v1alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -78,6 +79,12 @@ func (d *APIApp) IsReady() bool {
|
||||
}
|
||||
|
||||
func (d *APIApp) ConnectionRef(ctx context.Context, refresolver *RefResolver) (*ConnectionRef, error) {
|
||||
// Check if using direct Zitadel ID reference
|
||||
if d.Spec.ProjectRef.ID != "" {
|
||||
return &d.Spec.ProjectRef.ConnectionRef, nil
|
||||
}
|
||||
|
||||
// Fall back to K8s reference resolution
|
||||
project, err := refresolver.ProjectRef(ctx, &d.Spec.ProjectRef, d.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -88,24 +95,29 @@ func (d *APIApp) ConnectionRef(ctx context.Context, refresolver *RefResolver) (*
|
||||
}
|
||||
return &org.Spec.ConnectionRef, nil
|
||||
}
|
||||
func (d *APIApp) Organization(ctx context.Context, refresolver *RefResolver) (*Organization, error) {
|
||||
func (d *APIApp) Organization(ctx context.Context, refresolver *RefResolver) (*ResolvedReference, error) {
|
||||
// Check if using direct Zitadel ID reference for project
|
||||
if d.Spec.ProjectRef.ID != "" {
|
||||
// For cross-cluster references, we need to get the organization ID from the project reference
|
||||
// Since we don't have the full project object, we'll need to resolve the organization differently
|
||||
// For now, return an error as this requires more complex handling
|
||||
return nil, fmt.Errorf("cross-cluster organization resolution not yet implemented")
|
||||
}
|
||||
|
||||
// Fall back to K8s reference resolution
|
||||
project, err := refresolver.ProjectRef(ctx, &d.Spec.ProjectRef, d.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
org, err := refresolver.OrganizationRef(ctx, &project.Spec.OrganizationRef, d.Namespace)
|
||||
orgRef, err := refresolver.ResolveOrganization(ctx, &project.Spec.OrganizationRef, d.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return org, nil
|
||||
return orgRef, nil
|
||||
}
|
||||
|
||||
func (d *APIApp) Project(ctx context.Context, refresolver *RefResolver) (*Project, error) {
|
||||
project, err := refresolver.ProjectRef(ctx, &d.Spec.ProjectRef, d.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return project, nil
|
||||
func (d *APIApp) Project(ctx context.Context, refresolver *RefResolver) (*ResolvedReference, error) {
|
||||
return refresolver.ResolveProject(ctx, &d.Spec.ProjectRef, d.Namespace)
|
||||
}
|
||||
|
||||
//+kubebuilder:object:root=true
|
||||
|
||||
@@ -18,6 +18,7 @@ package v1alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
@@ -99,6 +100,12 @@ func (d *OIDCApp) IsReady() bool {
|
||||
}
|
||||
|
||||
func (d *OIDCApp) ConnectionRef(ctx context.Context, refresolver *RefResolver) (*ConnectionRef, error) {
|
||||
// Check if using direct Zitadel ID reference
|
||||
if d.Spec.ProjectRef.ID != "" {
|
||||
return &d.Spec.ProjectRef.ConnectionRef, nil
|
||||
}
|
||||
|
||||
// Fall back to K8s reference resolution
|
||||
project, err := refresolver.ProjectRef(ctx, &d.Spec.ProjectRef, d.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -111,24 +118,29 @@ func (d *OIDCApp) ConnectionRef(ctx context.Context, refresolver *RefResolver) (
|
||||
|
||||
return &org.Spec.ConnectionRef, nil
|
||||
}
|
||||
func (d *OIDCApp) Organization(ctx context.Context, refresolver *RefResolver) (*Organization, error) {
|
||||
func (d *OIDCApp) Organization(ctx context.Context, refresolver *RefResolver) (*ResolvedReference, error) {
|
||||
// Check if using direct Zitadel ID reference for project
|
||||
if d.Spec.ProjectRef.ID != "" {
|
||||
// For cross-cluster references, we need to get the organization ID from the project reference
|
||||
// Since we don't have the full project object, we'll need to resolve the organization differently
|
||||
// For now, return an error as this requires more complex handling
|
||||
return nil, fmt.Errorf("cross-cluster organization resolution not yet implemented")
|
||||
}
|
||||
|
||||
// Fall back to K8s reference resolution
|
||||
project, err := refresolver.ProjectRef(ctx, &d.Spec.ProjectRef, d.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
org, err := refresolver.OrganizationRef(ctx, &project.Spec.OrganizationRef, d.Namespace)
|
||||
orgRef, err := refresolver.ResolveOrganization(ctx, &project.Spec.OrganizationRef, d.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return org, nil
|
||||
return orgRef, nil
|
||||
}
|
||||
|
||||
func (d *OIDCApp) Project(ctx context.Context, refresolver *RefResolver) (*Project, error) {
|
||||
project, err := refresolver.ProjectRef(ctx, &d.Spec.ProjectRef, d.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return project, nil
|
||||
func (d *OIDCApp) Project(ctx context.Context, refresolver *RefResolver) (*ResolvedReference, error) {
|
||||
return refresolver.ResolveProject(ctx, &d.Spec.ProjectRef, d.Namespace)
|
||||
}
|
||||
|
||||
func (d *OIDCApp) ClientSecretName() string {
|
||||
|
||||
@@ -96,6 +96,12 @@ func (d *Project) IsReady() bool {
|
||||
}
|
||||
|
||||
func (d *Project) ConnectionRef(ctx context.Context, refresolver *RefResolver) (*ConnectionRef, error) {
|
||||
// Check if using direct Zitadel ID reference
|
||||
if d.Spec.OrganizationRef.ID != "" {
|
||||
return &d.Spec.OrganizationRef.ConnectionRef, nil
|
||||
}
|
||||
|
||||
// Fall back to K8s reference resolution
|
||||
org, err := refresolver.OrganizationRef(ctx, &d.Spec.OrganizationRef, d.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -5,31 +5,31 @@ import (
|
||||
)
|
||||
|
||||
type ConnectionRef struct {
|
||||
// ObjectReference is a reference to a object.
|
||||
// +operator-sdk:csv:customresourcedefinitions:type=spec
|
||||
corev1.ObjectReference `json:",inline"`
|
||||
ObjectReference corev1.ObjectReference `json:",inline"`
|
||||
}
|
||||
|
||||
type OIDCAppRef struct {
|
||||
// ObjectReference is a reference to a object.
|
||||
// +operator-sdk:csv:customresourcedefinitions:type=spec
|
||||
corev1.ObjectReference `json:",inline"`
|
||||
ObjectReference corev1.ObjectReference `json:",inline"`
|
||||
}
|
||||
|
||||
// OrganizationRef can reference an organization via K8s object or direct Zitadel ID
|
||||
// +kubebuilder:validation:XValidation:rule="has(self.name) == has(self.id)",message="must provide either k8s object reference (name) or zitadel ID reference (id), but not both"
|
||||
// +kubebuilder:validation:XValidation:rule="!has(self.id) || has(self.connectionRef.name)",message="zitadel ID reference requires connectionRef.name"
|
||||
type OrganizationRef struct {
|
||||
// ObjectReference is a reference to a object.
|
||||
// +operator-sdk:csv:customresourcedefinitions:type=spec
|
||||
corev1.ObjectReference `json:",inline"`
|
||||
ObjectReference corev1.ObjectReference `json:",inline"`
|
||||
ID string `json:"id,omitempty"`
|
||||
ConnectionRef ConnectionRef `json:"connectionRef,omitempty"`
|
||||
}
|
||||
|
||||
// ProjectRef can reference a project via K8s object or direct Zitadel ID
|
||||
// +kubebuilder:validation:XValidation:rule="has(self.name) == has(self.id)",message="must provide either k8s object reference (name) or zitadel ID reference (id), but not both"
|
||||
// +kubebuilder:validation:XValidation:rule="!has(self.id) || has(self.connectionRef.name)",message="zitadel ID reference requires connectionRef.name"
|
||||
type ProjectRef struct {
|
||||
// ObjectReference is a reference to a object.
|
||||
// +operator-sdk:csv:customresourcedefinitions:type=spec
|
||||
corev1.ObjectReference `json:",inline"`
|
||||
ObjectReference corev1.ObjectReference `json:",inline"`
|
||||
ID string `json:"id,omitempty"`
|
||||
ConnectionRef ConnectionRef `json:"connectionRef,omitempty"`
|
||||
}
|
||||
|
||||
type ActionRef struct {
|
||||
// ObjectReference is a reference to a object.
|
||||
// +operator-sdk:csv:customresourcedefinitions:type=spec
|
||||
corev1.ObjectReference `json:",inline"`
|
||||
ObjectReference corev1.ObjectReference `json:",inline"`
|
||||
}
|
||||
|
||||
@@ -9,6 +9,13 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
// ResolvedReference is the common abstraction for both reference types
|
||||
type ResolvedReference struct {
|
||||
ID string
|
||||
Namespace string
|
||||
Name string
|
||||
}
|
||||
|
||||
// +kubebuilder:object:generate=false
|
||||
type RefResolver struct {
|
||||
client client.Client
|
||||
@@ -22,16 +29,16 @@ func NewRefResolver(client client.Client) *RefResolver {
|
||||
|
||||
func (r *RefResolver) OIDCAppRef(ctx context.Context, ref *OIDCAppRef,
|
||||
namespace string) (*OIDCApp, error) {
|
||||
if ref.Kind != "" && ref.Kind != "OIDCApp" {
|
||||
return nil, fmt.Errorf("Unsupported reference kind: '%s'", ref.Kind)
|
||||
if ref.ObjectReference.Kind != "" && ref.ObjectReference.Kind != "OIDCApp" {
|
||||
return nil, fmt.Errorf("Unsupported reference kind: '%s'", ref.ObjectReference.Kind)
|
||||
}
|
||||
|
||||
key := types.NamespacedName{
|
||||
Name: ref.Name,
|
||||
Name: ref.ObjectReference.Name,
|
||||
Namespace: namespace,
|
||||
}
|
||||
if ref.Namespace != "" {
|
||||
key.Namespace = ref.Namespace
|
||||
if ref.ObjectReference.Namespace != "" {
|
||||
key.Namespace = ref.ObjectReference.Namespace
|
||||
}
|
||||
|
||||
var zitadel OIDCApp
|
||||
@@ -43,16 +50,16 @@ func (r *RefResolver) OIDCAppRef(ctx context.Context, ref *OIDCAppRef,
|
||||
|
||||
func (r *RefResolver) ActionRef(ctx context.Context, ref *ActionRef,
|
||||
namespace string) (*Action, error) {
|
||||
if ref.Kind != "" && ref.Kind != "Action" {
|
||||
return nil, fmt.Errorf("Unsupported reference kind: '%s'", ref.Kind)
|
||||
if ref.ObjectReference.Kind != "" && ref.ObjectReference.Kind != "Action" {
|
||||
return nil, fmt.Errorf("Unsupported reference kind: '%s'", ref.ObjectReference.Kind)
|
||||
}
|
||||
|
||||
key := types.NamespacedName{
|
||||
Name: ref.Name,
|
||||
Name: ref.ObjectReference.Name,
|
||||
Namespace: namespace,
|
||||
}
|
||||
if ref.Namespace != "" {
|
||||
key.Namespace = ref.Namespace
|
||||
if ref.ObjectReference.Namespace != "" {
|
||||
key.Namespace = ref.ObjectReference.Namespace
|
||||
}
|
||||
|
||||
var zitadel Action
|
||||
@@ -64,16 +71,16 @@ func (r *RefResolver) ActionRef(ctx context.Context, ref *ActionRef,
|
||||
|
||||
func (r *RefResolver) ProjectRef(ctx context.Context, ref *ProjectRef,
|
||||
namespace string) (*Project, error) {
|
||||
if ref.Kind != "" && ref.Kind != "Project" {
|
||||
return nil, fmt.Errorf("Unsupported reference kind: '%s'", ref.Kind)
|
||||
if ref.ObjectReference.Kind != "" && ref.ObjectReference.Kind != "Project" {
|
||||
return nil, fmt.Errorf("Unsupported reference kind: '%s'", ref.ObjectReference.Kind)
|
||||
}
|
||||
|
||||
key := types.NamespacedName{
|
||||
Name: ref.Name,
|
||||
Name: ref.ObjectReference.Name,
|
||||
Namespace: namespace,
|
||||
}
|
||||
if ref.Namespace != "" {
|
||||
key.Namespace = ref.Namespace
|
||||
if ref.ObjectReference.Namespace != "" {
|
||||
key.Namespace = ref.ObjectReference.Namespace
|
||||
}
|
||||
|
||||
var zitadel Project
|
||||
@@ -85,16 +92,16 @@ func (r *RefResolver) ProjectRef(ctx context.Context, ref *ProjectRef,
|
||||
|
||||
func (r *RefResolver) OrganizationRef(ctx context.Context, ref *OrganizationRef,
|
||||
namespace string) (*Organization, error) {
|
||||
if ref.Kind != "" && ref.Kind != "Organization" {
|
||||
return nil, fmt.Errorf("Unsupported reference kind: '%s'", ref.Kind)
|
||||
if ref.ObjectReference.Kind != "" && ref.ObjectReference.Kind != "Organization" {
|
||||
return nil, fmt.Errorf("Unsupported reference kind: '%s'", ref.ObjectReference.Kind)
|
||||
}
|
||||
|
||||
key := types.NamespacedName{
|
||||
Name: ref.Name,
|
||||
Name: ref.ObjectReference.Name,
|
||||
Namespace: namespace,
|
||||
}
|
||||
if ref.Namespace != "" {
|
||||
key.Namespace = ref.Namespace
|
||||
if ref.ObjectReference.Namespace != "" {
|
||||
key.Namespace = ref.ObjectReference.Namespace
|
||||
}
|
||||
|
||||
var zitadel Organization
|
||||
@@ -105,15 +112,15 @@ func (r *RefResolver) OrganizationRef(ctx context.Context, ref *OrganizationRef,
|
||||
}
|
||||
|
||||
func (r *RefResolver) ConnectionRef(ctx context.Context, ref *ConnectionRef, namespace string) (*Connection, error) {
|
||||
if ref.Kind != "" && ref.Kind != "Connection" {
|
||||
return nil, fmt.Errorf("Unsupported reference kind: '%s'", ref.Kind)
|
||||
if ref.ObjectReference.Kind != "" && ref.ObjectReference.Kind != "Connection" {
|
||||
return nil, fmt.Errorf("Unsupported reference kind: '%s'", ref.ObjectReference.Kind)
|
||||
}
|
||||
key := types.NamespacedName{
|
||||
Name: ref.Name,
|
||||
Name: ref.ObjectReference.Name,
|
||||
Namespace: namespace,
|
||||
}
|
||||
if ref.Namespace != "" {
|
||||
key.Namespace = ref.Namespace
|
||||
if ref.ObjectReference.Namespace != "" {
|
||||
key.Namespace = ref.ObjectReference.Namespace
|
||||
}
|
||||
|
||||
var connection Connection
|
||||
@@ -141,3 +148,79 @@ func (r *RefResolver) SecretKeyRef(ctx context.Context, selector corev1.SecretKe
|
||||
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
// ResolveOrganization resolves an organization reference to a common abstraction
|
||||
func (r *RefResolver) ResolveOrganization(ctx context.Context, ref *OrganizationRef, namespace string) (*ResolvedReference, error) {
|
||||
// Priority 1: Direct Zitadel ID reference (cross-cluster)
|
||||
if ref.ID != "" {
|
||||
return &ResolvedReference{
|
||||
ID: ref.ID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Priority 2: K8s object reference (same-cluster, backward compatible)
|
||||
if ref.ObjectReference.Name != "" {
|
||||
org, err := r.OrganizationRef(ctx, ref, namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if org.Status.OrganizationId == nil {
|
||||
return nil, fmt.Errorf("organization not ready")
|
||||
}
|
||||
return &ResolvedReference{
|
||||
ID: *org.Status.OrganizationId,
|
||||
Namespace: org.Namespace,
|
||||
Name: org.Name,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("no valid organization reference provided")
|
||||
}
|
||||
|
||||
// ResolveProject resolves a project reference to a common abstraction
|
||||
func (r *RefResolver) ResolveProject(ctx context.Context, ref *ProjectRef, namespace string) (*ResolvedReference, error) {
|
||||
// Priority 1: Direct Zitadel ID reference (cross-cluster)
|
||||
if ref.ID != "" {
|
||||
return &ResolvedReference{
|
||||
ID: ref.ID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Priority 2: K8s object reference (same-cluster, backward compatible)
|
||||
if ref.ObjectReference.Name != "" {
|
||||
project, err := r.ProjectRef(ctx, ref, namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if project.Status.ProjectId == nil {
|
||||
return nil, fmt.Errorf("project not ready")
|
||||
}
|
||||
return &ResolvedReference{
|
||||
ID: *project.Status.ProjectId,
|
||||
Namespace: project.Namespace,
|
||||
Name: project.Name,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("no valid project reference provided")
|
||||
}
|
||||
|
||||
// ResolveConnectionForRef resolves connection from either a ConnectionRef or enhanced reference with embedded ConnectionRef
|
||||
func (r *RefResolver) ResolveConnectionForRef(ctx context.Context, ref interface{}, namespace string) (*Connection, error) {
|
||||
switch v := ref.(type) {
|
||||
case *ConnectionRef:
|
||||
return r.ConnectionRef(ctx, v, namespace)
|
||||
case *OrganizationRef:
|
||||
if v.ID != "" {
|
||||
return r.ConnectionRef(ctx, &v.ConnectionRef, namespace)
|
||||
}
|
||||
return nil, fmt.Errorf("organization reference does not contain connection info")
|
||||
case *ProjectRef:
|
||||
if v.ID != "" {
|
||||
return r.ConnectionRef(ctx, &v.ConnectionRef, namespace)
|
||||
}
|
||||
return nil, fmt.Errorf("project reference does not contain connection info")
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported reference type for connection resolution")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user