From 93d570925f3bc6ddd8adf13fe5e4bb2e213078fd Mon Sep 17 00:00:00 2001 From: Yevhen Artemenko Date: Sat, 22 Aug 2026 17:07:19 +0300 Subject: [PATCH] feat: add domainIdRef/domainIdSelector support to identity resources Signed-off-by: Yevhen Artemenko --- .../identity/v1alpha1/projectv3_extractor.go | 30 ++ .../v1alpha1/zz_generated.deepcopy.go | 120 +++++ .../v1alpha1/zz_generated.resolvers.go | 234 +++++++++ .../identity/v1alpha1/zz_groupv3_types.go | 20 + .../zz_inheritroleassignmentv3_types.go | 20 + .../identity/v1alpha1/zz_projectv3_types.go | 20 + .../v1alpha1/zz_roleassignmentv3_types.go | 20 + .../identity/v1alpha1/zz_rolev3_types.go | 20 + .../identity/v1alpha1/zz_userv3_types.go | 20 + .../identity/v1alpha1/projectv3_extractor.go | 30 ++ .../v1alpha1/zz_generated.deepcopy.go | 120 +++++ .../v1alpha1/zz_generated.resolvers.go | 234 +++++++++ .../identity/v1alpha1/zz_groupv3_types.go | 20 + .../zz_inheritroleassignmentv3_types.go | 20 + .../identity/v1alpha1/zz_projectv3_types.go | 20 + .../v1alpha1/zz_roleassignmentv3_types.go | 20 + .../identity/v1alpha1/zz_rolev3_types.go | 20 + .../identity/v1alpha1/zz_userv3_types.go | 20 + config/cluster/identity/config.go | 48 +- config/namespaced/identity/config.go | 48 +- .../v1alpha1/inheritroleassignmentv3.yaml | 12 +- .../v1alpha1/inheritroleassignmentv3.yaml | 12 +- .../cluster/identity/domain-ref-examples.yaml | 113 +++++ .../cluster/identity/groupv3/zz_controller.go | 3 + .../inheritroleassignmentv3/zz_controller.go | 3 + .../identity/projectv3/zz_controller.go | 3 + .../roleassignmentv3/zz_controller.go | 3 + .../cluster/identity/rolev3/zz_controller.go | 3 + .../cluster/identity/userv3/zz_controller.go | 3 + .../identity/groupv3/zz_controller.go | 3 + .../inheritroleassignmentv3/zz_controller.go | 3 + .../identity/projectv3/zz_controller.go | 3 + .../roleassignmentv3/zz_controller.go | 3 + .../identity/rolev3/zz_controller.go | 3 + .../identity/userv3/zz_controller.go | 3 + internal/initializer/domain/domain.go | 191 ++++++++ internal/initializer/domain/domain_test.go | 445 ++++++++++++++++++ ...tity.openstack.crossplane.io_groupv3s.yaml | 152 ++++++ ...rossplane.io_inheritroleassignmentv3s.yaml | 152 ++++++ ...ty.openstack.crossplane.io_projectv3s.yaml | 152 ++++++ ...stack.crossplane.io_roleassignmentv3s.yaml | 152 ++++++ ...ntity.openstack.crossplane.io_rolev3s.yaml | 152 ++++++ ...ntity.openstack.crossplane.io_userv3s.yaml | 152 ++++++ ...ty.openstack.m.crossplane.io_groupv3s.yaml | 164 +++++++ ...rossplane.io_inheritroleassignmentv3s.yaml | 164 +++++++ ....openstack.m.crossplane.io_projectv3s.yaml | 164 +++++++ ...ack.m.crossplane.io_roleassignmentv3s.yaml | 164 +++++++ ...ity.openstack.m.crossplane.io_rolev3s.yaml | 164 +++++++ ...ity.openstack.m.crossplane.io_userv3s.yaml | 164 +++++++ 49 files changed, 3801 insertions(+), 8 deletions(-) create mode 100644 apis/cluster/identity/v1alpha1/projectv3_extractor.go create mode 100644 apis/namespaced/identity/v1alpha1/projectv3_extractor.go create mode 100644 examples/cluster/identity/domain-ref-examples.yaml create mode 100644 internal/initializer/domain/domain.go create mode 100644 internal/initializer/domain/domain_test.go diff --git a/apis/cluster/identity/v1alpha1/projectv3_extractor.go b/apis/cluster/identity/v1alpha1/projectv3_extractor.go new file mode 100644 index 00000000..eb6af7cb --- /dev/null +++ b/apis/cluster/identity/v1alpha1/projectv3_extractor.go @@ -0,0 +1,30 @@ +/* +Copyright 2022 Upbound Inc. +Copyright 2023 Jakob Schlagenhaufer +Copyright 2025 Yannick Schlosser +*/ + +package v1alpha1 + +import ( + "github.com/crossplane/crossplane-runtime/v2/pkg/reference" + "github.com/crossplane/crossplane-runtime/v2/pkg/resource" +) + +// ExtractDomainID returns the external name of a ProjectV3 only when it +// represents a domain (isDomain: true). Referencing a regular project +// yields an empty value, causing reference resolution to fail. +func ExtractDomainID() reference.ExtractValueFn { + return func(mg resource.Managed) string { + p, ok := any(mg).(*ProjectV3) + if !ok { + return "" + } + isDomain := (p.Spec.ForProvider.IsDomain != nil && *p.Spec.ForProvider.IsDomain) || + (p.Status.AtProvider.IsDomain != nil && *p.Status.AtProvider.IsDomain) + if !isDomain { + return "" + } + return reference.ExternalName()(mg) + } +} diff --git a/apis/cluster/identity/v1alpha1/zz_generated.deepcopy.go b/apis/cluster/identity/v1alpha1/zz_generated.deepcopy.go index 5fe55a8b..01f41a55 100644 --- a/apis/cluster/identity/v1alpha1/zz_generated.deepcopy.go +++ b/apis/cluster/identity/v1alpha1/zz_generated.deepcopy.go @@ -922,6 +922,16 @@ func (in *GroupV3InitParameters) DeepCopyInto(out *GroupV3InitParameters) { *out = new(string) **out = **in } + if in.DomainIDRef != nil { + in, out := &in.DomainIDRef, &out.DomainIDRef + *out = new(v1.Reference) + (*in).DeepCopyInto(*out) + } + if in.DomainIDSelector != nil { + in, out := &in.DomainIDSelector, &out.DomainIDSelector + *out = new(v1.Selector) + (*in).DeepCopyInto(*out) + } if in.Name != nil { in, out := &in.Name, &out.Name *out = new(string) @@ -1029,6 +1039,16 @@ func (in *GroupV3Parameters) DeepCopyInto(out *GroupV3Parameters) { *out = new(string) **out = **in } + if in.DomainIDRef != nil { + in, out := &in.DomainIDRef, &out.DomainIDRef + *out = new(v1.Reference) + (*in).DeepCopyInto(*out) + } + if in.DomainIDSelector != nil { + in, out := &in.DomainIDSelector, &out.DomainIDSelector + *out = new(v1.Selector) + (*in).DeepCopyInto(*out) + } if in.Name != nil { in, out := &in.Name, &out.Name *out = new(string) @@ -1121,6 +1141,16 @@ func (in *InheritRoleAssignmentV3InitParameters) DeepCopyInto(out *InheritRoleAs *out = new(string) **out = **in } + if in.DomainIDRef != nil { + in, out := &in.DomainIDRef, &out.DomainIDRef + *out = new(v1.Reference) + (*in).DeepCopyInto(*out) + } + if in.DomainIDSelector != nil { + in, out := &in.DomainIDSelector, &out.DomainIDSelector + *out = new(v1.Selector) + (*in).DeepCopyInto(*out) + } if in.GroupID != nil { in, out := &in.GroupID, &out.GroupID *out = new(string) @@ -1268,6 +1298,16 @@ func (in *InheritRoleAssignmentV3Parameters) DeepCopyInto(out *InheritRoleAssign *out = new(string) **out = **in } + if in.DomainIDRef != nil { + in, out := &in.DomainIDRef, &out.DomainIDRef + *out = new(v1.Reference) + (*in).DeepCopyInto(*out) + } + if in.DomainIDSelector != nil { + in, out := &in.DomainIDSelector, &out.DomainIDSelector + *out = new(v1.Selector) + (*in).DeepCopyInto(*out) + } if in.GroupID != nil { in, out := &in.GroupID, &out.GroupID *out = new(string) @@ -1478,6 +1518,16 @@ func (in *ProjectV3InitParameters) DeepCopyInto(out *ProjectV3InitParameters) { *out = new(string) **out = **in } + if in.DomainIDRef != nil { + in, out := &in.DomainIDRef, &out.DomainIDRef + *out = new(v1.Reference) + (*in).DeepCopyInto(*out) + } + if in.DomainIDSelector != nil { + in, out := &in.DomainIDSelector, &out.DomainIDSelector + *out = new(v1.Selector) + (*in).DeepCopyInto(*out) + } if in.Enabled != nil { in, out := &in.Enabled, &out.Enabled *out = new(bool) @@ -1637,6 +1687,16 @@ func (in *ProjectV3Parameters) DeepCopyInto(out *ProjectV3Parameters) { *out = new(string) **out = **in } + if in.DomainIDRef != nil { + in, out := &in.DomainIDRef, &out.DomainIDRef + *out = new(v1.Reference) + (*in).DeepCopyInto(*out) + } + if in.DomainIDSelector != nil { + in, out := &in.DomainIDSelector, &out.DomainIDSelector + *out = new(v1.Selector) + (*in).DeepCopyInto(*out) + } if in.Enabled != nil { in, out := &in.Enabled, &out.Enabled *out = new(bool) @@ -1755,6 +1815,16 @@ func (in *RoleAssignmentV3InitParameters) DeepCopyInto(out *RoleAssignmentV3Init *out = new(string) **out = **in } + if in.DomainIDRef != nil { + in, out := &in.DomainIDRef, &out.DomainIDRef + *out = new(v1.Reference) + (*in).DeepCopyInto(*out) + } + if in.DomainIDSelector != nil { + in, out := &in.DomainIDSelector, &out.DomainIDSelector + *out = new(v1.Selector) + (*in).DeepCopyInto(*out) + } if in.GroupID != nil { in, out := &in.GroupID, &out.GroupID *out = new(string) @@ -1912,6 +1982,16 @@ func (in *RoleAssignmentV3Parameters) DeepCopyInto(out *RoleAssignmentV3Paramete *out = new(string) **out = **in } + if in.DomainIDRef != nil { + in, out := &in.DomainIDRef, &out.DomainIDRef + *out = new(v1.Reference) + (*in).DeepCopyInto(*out) + } + if in.DomainIDSelector != nil { + in, out := &in.DomainIDSelector, &out.DomainIDSelector + *out = new(v1.Selector) + (*in).DeepCopyInto(*out) + } if in.GroupID != nil { in, out := &in.GroupID, &out.GroupID *out = new(string) @@ -2049,6 +2129,16 @@ func (in *RoleV3InitParameters) DeepCopyInto(out *RoleV3InitParameters) { *out = new(string) **out = **in } + if in.DomainIDRef != nil { + in, out := &in.DomainIDRef, &out.DomainIDRef + *out = new(v1.Reference) + (*in).DeepCopyInto(*out) + } + if in.DomainIDSelector != nil { + in, out := &in.DomainIDSelector, &out.DomainIDSelector + *out = new(v1.Selector) + (*in).DeepCopyInto(*out) + } if in.Name != nil { in, out := &in.Name, &out.Name *out = new(string) @@ -2146,6 +2236,16 @@ func (in *RoleV3Parameters) DeepCopyInto(out *RoleV3Parameters) { *out = new(string) **out = **in } + if in.DomainIDRef != nil { + in, out := &in.DomainIDRef, &out.DomainIDRef + *out = new(v1.Reference) + (*in).DeepCopyInto(*out) + } + if in.DomainIDSelector != nil { + in, out := &in.DomainIDSelector, &out.DomainIDSelector + *out = new(v1.Selector) + (*in).DeepCopyInto(*out) + } if in.Name != nil { in, out := &in.Name, &out.Name *out = new(string) @@ -2706,6 +2806,16 @@ func (in *UserV3InitParameters) DeepCopyInto(out *UserV3InitParameters) { *out = new(string) **out = **in } + if in.DomainIDRef != nil { + in, out := &in.DomainIDRef, &out.DomainIDRef + *out = new(v1.Reference) + (*in).DeepCopyInto(*out) + } + if in.DomainIDSelector != nil { + in, out := &in.DomainIDSelector, &out.DomainIDSelector + *out = new(v1.Selector) + (*in).DeepCopyInto(*out) + } if in.Enabled != nil { in, out := &in.Enabled, &out.Enabled *out = new(bool) @@ -2934,6 +3044,16 @@ func (in *UserV3Parameters) DeepCopyInto(out *UserV3Parameters) { *out = new(string) **out = **in } + if in.DomainIDRef != nil { + in, out := &in.DomainIDRef, &out.DomainIDRef + *out = new(v1.Reference) + (*in).DeepCopyInto(*out) + } + if in.DomainIDSelector != nil { + in, out := &in.DomainIDSelector, &out.DomainIDSelector + *out = new(v1.Selector) + (*in).DeepCopyInto(*out) + } if in.Enabled != nil { in, out := &in.Enabled, &out.Enabled *out = new(bool) diff --git a/apis/cluster/identity/v1alpha1/zz_generated.resolvers.go b/apis/cluster/identity/v1alpha1/zz_generated.resolvers.go index f45b8370..dc85ba1b 100644 --- a/apis/cluster/identity/v1alpha1/zz_generated.resolvers.go +++ b/apis/cluster/identity/v1alpha1/zz_generated.resolvers.go @@ -94,6 +94,50 @@ func (mg *EndpointV3) ResolveReferences(ctx context.Context, c client.Reader) er return nil } +// ResolveReferences of this GroupV3. +func (mg *GroupV3) ResolveReferences(ctx context.Context, c client.Reader) error { + r := reference.NewAPIResolver(c, mg) + + var rsp reference.ResolutionResponse + var err error + + rsp, err = r.Resolve(ctx, reference.ResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.ForProvider.DomainID), + Extract: ExtractDomainID(), + Namespace: mg.GetNamespace(), + Reference: mg.Spec.ForProvider.DomainIDRef, + Selector: mg.Spec.ForProvider.DomainIDSelector, + To: reference.To{ + List: &ProjectV3List{}, + Managed: &ProjectV3{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.ForProvider.DomainID") + } + mg.Spec.ForProvider.DomainID = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.ForProvider.DomainIDRef = rsp.ResolvedReference + + rsp, err = r.Resolve(ctx, reference.ResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.InitProvider.DomainID), + Extract: ExtractDomainID(), + Namespace: mg.GetNamespace(), + Reference: mg.Spec.InitProvider.DomainIDRef, + Selector: mg.Spec.InitProvider.DomainIDSelector, + To: reference.To{ + List: &ProjectV3List{}, + Managed: &ProjectV3{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.InitProvider.DomainID") + } + mg.Spec.InitProvider.DomainID = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.InitProvider.DomainIDRef = rsp.ResolvedReference + + return nil +} + // ResolveReferences of this InheritRoleAssignmentV3. func (mg *InheritRoleAssignmentV3) ResolveReferences(ctx context.Context, c client.Reader) error { r := reference.NewAPIResolver(c, mg) @@ -101,6 +145,23 @@ func (mg *InheritRoleAssignmentV3) ResolveReferences(ctx context.Context, c clie var rsp reference.ResolutionResponse var err error + rsp, err = r.Resolve(ctx, reference.ResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.ForProvider.DomainID), + Extract: ExtractDomainID(), + Namespace: mg.GetNamespace(), + Reference: mg.Spec.ForProvider.DomainIDRef, + Selector: mg.Spec.ForProvider.DomainIDSelector, + To: reference.To{ + List: &ProjectV3List{}, + Managed: &ProjectV3{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.ForProvider.DomainID") + } + mg.Spec.ForProvider.DomainID = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.ForProvider.DomainIDRef = rsp.ResolvedReference + rsp, err = r.Resolve(ctx, reference.ResolutionRequest{ CurrentValue: reference.FromPtrValue(mg.Spec.ForProvider.RoleID), Extract: resource.ExtractResourceID(), @@ -135,6 +196,23 @@ func (mg *InheritRoleAssignmentV3) ResolveReferences(ctx context.Context, c clie mg.Spec.ForProvider.UserID = reference.ToPtrValue(rsp.ResolvedValue) mg.Spec.ForProvider.UserIDRef = rsp.ResolvedReference + rsp, err = r.Resolve(ctx, reference.ResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.InitProvider.DomainID), + Extract: ExtractDomainID(), + Namespace: mg.GetNamespace(), + Reference: mg.Spec.InitProvider.DomainIDRef, + Selector: mg.Spec.InitProvider.DomainIDSelector, + To: reference.To{ + List: &ProjectV3List{}, + Managed: &ProjectV3{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.InitProvider.DomainID") + } + mg.Spec.InitProvider.DomainID = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.InitProvider.DomainIDRef = rsp.ResolvedReference + rsp, err = r.Resolve(ctx, reference.ResolutionRequest{ CurrentValue: reference.FromPtrValue(mg.Spec.InitProvider.RoleID), Extract: resource.ExtractResourceID(), @@ -172,6 +250,50 @@ func (mg *InheritRoleAssignmentV3) ResolveReferences(ctx context.Context, c clie return nil } +// ResolveReferences of this ProjectV3. +func (mg *ProjectV3) ResolveReferences(ctx context.Context, c client.Reader) error { + r := reference.NewAPIResolver(c, mg) + + var rsp reference.ResolutionResponse + var err error + + rsp, err = r.Resolve(ctx, reference.ResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.ForProvider.DomainID), + Extract: ExtractDomainID(), + Namespace: mg.GetNamespace(), + Reference: mg.Spec.ForProvider.DomainIDRef, + Selector: mg.Spec.ForProvider.DomainIDSelector, + To: reference.To{ + List: &ProjectV3List{}, + Managed: &ProjectV3{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.ForProvider.DomainID") + } + mg.Spec.ForProvider.DomainID = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.ForProvider.DomainIDRef = rsp.ResolvedReference + + rsp, err = r.Resolve(ctx, reference.ResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.InitProvider.DomainID), + Extract: ExtractDomainID(), + Namespace: mg.GetNamespace(), + Reference: mg.Spec.InitProvider.DomainIDRef, + Selector: mg.Spec.InitProvider.DomainIDSelector, + To: reference.To{ + List: &ProjectV3List{}, + Managed: &ProjectV3{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.InitProvider.DomainID") + } + mg.Spec.InitProvider.DomainID = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.InitProvider.DomainIDRef = rsp.ResolvedReference + + return nil +} + // ResolveReferences of this RoleAssignmentV3. func (mg *RoleAssignmentV3) ResolveReferences(ctx context.Context, c client.Reader) error { r := reference.NewAPIResolver(c, mg) @@ -179,6 +301,23 @@ func (mg *RoleAssignmentV3) ResolveReferences(ctx context.Context, c client.Read var rsp reference.ResolutionResponse var err error + rsp, err = r.Resolve(ctx, reference.ResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.ForProvider.DomainID), + Extract: ExtractDomainID(), + Namespace: mg.GetNamespace(), + Reference: mg.Spec.ForProvider.DomainIDRef, + Selector: mg.Spec.ForProvider.DomainIDSelector, + To: reference.To{ + List: &ProjectV3List{}, + Managed: &ProjectV3{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.ForProvider.DomainID") + } + mg.Spec.ForProvider.DomainID = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.ForProvider.DomainIDRef = rsp.ResolvedReference + rsp, err = r.Resolve(ctx, reference.ResolutionRequest{ CurrentValue: reference.FromPtrValue(mg.Spec.ForProvider.ProjectID), Extract: reference.ExternalName(), @@ -230,6 +369,23 @@ func (mg *RoleAssignmentV3) ResolveReferences(ctx context.Context, c client.Read mg.Spec.ForProvider.UserID = reference.ToPtrValue(rsp.ResolvedValue) mg.Spec.ForProvider.UserIDRef = rsp.ResolvedReference + rsp, err = r.Resolve(ctx, reference.ResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.InitProvider.DomainID), + Extract: ExtractDomainID(), + Namespace: mg.GetNamespace(), + Reference: mg.Spec.InitProvider.DomainIDRef, + Selector: mg.Spec.InitProvider.DomainIDSelector, + To: reference.To{ + List: &ProjectV3List{}, + Managed: &ProjectV3{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.InitProvider.DomainID") + } + mg.Spec.InitProvider.DomainID = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.InitProvider.DomainIDRef = rsp.ResolvedReference + rsp, err = r.Resolve(ctx, reference.ResolutionRequest{ CurrentValue: reference.FromPtrValue(mg.Spec.InitProvider.ProjectID), Extract: reference.ExternalName(), @@ -284,6 +440,50 @@ func (mg *RoleAssignmentV3) ResolveReferences(ctx context.Context, c client.Read return nil } +// ResolveReferences of this RoleV3. +func (mg *RoleV3) ResolveReferences(ctx context.Context, c client.Reader) error { + r := reference.NewAPIResolver(c, mg) + + var rsp reference.ResolutionResponse + var err error + + rsp, err = r.Resolve(ctx, reference.ResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.ForProvider.DomainID), + Extract: ExtractDomainID(), + Namespace: mg.GetNamespace(), + Reference: mg.Spec.ForProvider.DomainIDRef, + Selector: mg.Spec.ForProvider.DomainIDSelector, + To: reference.To{ + List: &ProjectV3List{}, + Managed: &ProjectV3{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.ForProvider.DomainID") + } + mg.Spec.ForProvider.DomainID = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.ForProvider.DomainIDRef = rsp.ResolvedReference + + rsp, err = r.Resolve(ctx, reference.ResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.InitProvider.DomainID), + Extract: ExtractDomainID(), + Namespace: mg.GetNamespace(), + Reference: mg.Spec.InitProvider.DomainIDRef, + Selector: mg.Spec.InitProvider.DomainIDSelector, + To: reference.To{ + List: &ProjectV3List{}, + Managed: &ProjectV3{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.InitProvider.DomainID") + } + mg.Spec.InitProvider.DomainID = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.InitProvider.DomainIDRef = rsp.ResolvedReference + + return nil +} + // ResolveReferences of this UserMembershipV3. func (mg *UserMembershipV3) ResolveReferences(ctx context.Context, c client.Reader) error { r := reference.NewAPIResolver(c, mg) @@ -386,6 +586,23 @@ func (mg *UserV3) ResolveReferences(ctx context.Context, c client.Reader) error mg.Spec.ForProvider.DefaultProjectID = reference.ToPtrValue(rsp.ResolvedValue) mg.Spec.ForProvider.DefaultProjectIDRef = rsp.ResolvedReference + rsp, err = r.Resolve(ctx, reference.ResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.ForProvider.DomainID), + Extract: ExtractDomainID(), + Namespace: mg.GetNamespace(), + Reference: mg.Spec.ForProvider.DomainIDRef, + Selector: mg.Spec.ForProvider.DomainIDSelector, + To: reference.To{ + List: &ProjectV3List{}, + Managed: &ProjectV3{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.ForProvider.DomainID") + } + mg.Spec.ForProvider.DomainID = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.ForProvider.DomainIDRef = rsp.ResolvedReference + rsp, err = r.Resolve(ctx, reference.ResolutionRequest{ CurrentValue: reference.FromPtrValue(mg.Spec.InitProvider.DefaultProjectID), Extract: resource.ExtractResourceID(), @@ -403,5 +620,22 @@ func (mg *UserV3) ResolveReferences(ctx context.Context, c client.Reader) error mg.Spec.InitProvider.DefaultProjectID = reference.ToPtrValue(rsp.ResolvedValue) mg.Spec.InitProvider.DefaultProjectIDRef = rsp.ResolvedReference + rsp, err = r.Resolve(ctx, reference.ResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.InitProvider.DomainID), + Extract: ExtractDomainID(), + Namespace: mg.GetNamespace(), + Reference: mg.Spec.InitProvider.DomainIDRef, + Selector: mg.Spec.InitProvider.DomainIDSelector, + To: reference.To{ + List: &ProjectV3List{}, + Managed: &ProjectV3{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.InitProvider.DomainID") + } + mg.Spec.InitProvider.DomainID = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.InitProvider.DomainIDRef = rsp.ResolvedReference + return nil } diff --git a/apis/cluster/identity/v1alpha1/zz_groupv3_types.go b/apis/cluster/identity/v1alpha1/zz_groupv3_types.go index 8c15d758..4eb89fc1 100755 --- a/apis/cluster/identity/v1alpha1/zz_groupv3_types.go +++ b/apis/cluster/identity/v1alpha1/zz_groupv3_types.go @@ -22,8 +22,18 @@ type GroupV3InitParameters struct { Description *string `json:"description,omitempty" tf:"description,omitempty"` // The domain the group belongs to. + // +crossplane:generate:reference:type=github.com/crossplane-contrib/provider-openstack/apis/cluster/identity/v1alpha1.ProjectV3 + // +crossplane:generate:reference:extractor=github.com/crossplane-contrib/provider-openstack/apis/cluster/identity/v1alpha1.ExtractDomainID() DomainID *string `json:"domainId,omitempty" tf:"domain_id,omitempty"` + // Reference to a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDRef *v1.Reference `json:"domainIdRef,omitempty" tf:"-"` + + // Selector for a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDSelector *v1.Selector `json:"domainIdSelector,omitempty" tf:"-"` + // The name of the group. Name *string `json:"name,omitempty" tf:"name,omitempty"` @@ -59,9 +69,19 @@ type GroupV3Parameters struct { Description *string `json:"description,omitempty" tf:"description,omitempty"` // The domain the group belongs to. + // +crossplane:generate:reference:type=github.com/crossplane-contrib/provider-openstack/apis/cluster/identity/v1alpha1.ProjectV3 + // +crossplane:generate:reference:extractor=github.com/crossplane-contrib/provider-openstack/apis/cluster/identity/v1alpha1.ExtractDomainID() // +kubebuilder:validation:Optional DomainID *string `json:"domainId,omitempty" tf:"domain_id,omitempty"` + // Reference to a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDRef *v1.Reference `json:"domainIdRef,omitempty" tf:"-"` + + // Selector for a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDSelector *v1.Selector `json:"domainIdSelector,omitempty" tf:"-"` + // The name of the group. // +kubebuilder:validation:Optional Name *string `json:"name,omitempty" tf:"name,omitempty"` diff --git a/apis/cluster/identity/v1alpha1/zz_inheritroleassignmentv3_types.go b/apis/cluster/identity/v1alpha1/zz_inheritroleassignmentv3_types.go index 41921b6e..ca403137 100755 --- a/apis/cluster/identity/v1alpha1/zz_inheritroleassignmentv3_types.go +++ b/apis/cluster/identity/v1alpha1/zz_inheritroleassignmentv3_types.go @@ -19,8 +19,18 @@ import ( type InheritRoleAssignmentV3InitParameters struct { // The domain to assign the role in. + // +crossplane:generate:reference:type=github.com/crossplane-contrib/provider-openstack/apis/cluster/identity/v1alpha1.ProjectV3 + // +crossplane:generate:reference:extractor=github.com/crossplane-contrib/provider-openstack/apis/cluster/identity/v1alpha1.ExtractDomainID() DomainID *string `json:"domainId,omitempty" tf:"domain_id,omitempty"` + // Reference to a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDRef *v1.Reference `json:"domainIdRef,omitempty" tf:"-"` + + // Selector for a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDSelector *v1.Selector `json:"domainIdSelector,omitempty" tf:"-"` + // The group to assign the role to. GroupID *string `json:"groupId,omitempty" tf:"group_id,omitempty"` @@ -89,9 +99,19 @@ type InheritRoleAssignmentV3Observation struct { type InheritRoleAssignmentV3Parameters struct { // The domain to assign the role in. + // +crossplane:generate:reference:type=github.com/crossplane-contrib/provider-openstack/apis/cluster/identity/v1alpha1.ProjectV3 + // +crossplane:generate:reference:extractor=github.com/crossplane-contrib/provider-openstack/apis/cluster/identity/v1alpha1.ExtractDomainID() // +kubebuilder:validation:Optional DomainID *string `json:"domainId,omitempty" tf:"domain_id,omitempty"` + // Reference to a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDRef *v1.Reference `json:"domainIdRef,omitempty" tf:"-"` + + // Selector for a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDSelector *v1.Selector `json:"domainIdSelector,omitempty" tf:"-"` + // The group to assign the role to. // +kubebuilder:validation:Optional GroupID *string `json:"groupId,omitempty" tf:"group_id,omitempty"` diff --git a/apis/cluster/identity/v1alpha1/zz_projectv3_types.go b/apis/cluster/identity/v1alpha1/zz_projectv3_types.go index 0d1e4db4..3ebc50a4 100755 --- a/apis/cluster/identity/v1alpha1/zz_projectv3_types.go +++ b/apis/cluster/identity/v1alpha1/zz_projectv3_types.go @@ -22,8 +22,18 @@ type ProjectV3InitParameters struct { Description *string `json:"description,omitempty" tf:"description,omitempty"` // The domain this project belongs to. + // +crossplane:generate:reference:type=github.com/crossplane-contrib/provider-openstack/apis/cluster/identity/v1alpha1.ProjectV3 + // +crossplane:generate:reference:extractor=github.com/crossplane-contrib/provider-openstack/apis/cluster/identity/v1alpha1.ExtractDomainID() DomainID *string `json:"domainId,omitempty" tf:"domain_id,omitempty"` + // Reference to a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDRef *v1.Reference `json:"domainIdRef,omitempty" tf:"-"` + + // Selector for a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDSelector *v1.Selector `json:"domainIdSelector,omitempty" tf:"-"` + // Whether the project is enabled or disabled. Valid // values are true and false. Default is true. Enabled *bool `json:"enabled,omitempty" tf:"enabled,omitempty"` @@ -95,9 +105,19 @@ type ProjectV3Parameters struct { Description *string `json:"description,omitempty" tf:"description,omitempty"` // The domain this project belongs to. + // +crossplane:generate:reference:type=github.com/crossplane-contrib/provider-openstack/apis/cluster/identity/v1alpha1.ProjectV3 + // +crossplane:generate:reference:extractor=github.com/crossplane-contrib/provider-openstack/apis/cluster/identity/v1alpha1.ExtractDomainID() // +kubebuilder:validation:Optional DomainID *string `json:"domainId,omitempty" tf:"domain_id,omitempty"` + // Reference to a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDRef *v1.Reference `json:"domainIdRef,omitempty" tf:"-"` + + // Selector for a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDSelector *v1.Selector `json:"domainIdSelector,omitempty" tf:"-"` + // Whether the project is enabled or disabled. Valid // values are true and false. Default is true. // +kubebuilder:validation:Optional diff --git a/apis/cluster/identity/v1alpha1/zz_roleassignmentv3_types.go b/apis/cluster/identity/v1alpha1/zz_roleassignmentv3_types.go index 493d69ad..73b7d14e 100755 --- a/apis/cluster/identity/v1alpha1/zz_roleassignmentv3_types.go +++ b/apis/cluster/identity/v1alpha1/zz_roleassignmentv3_types.go @@ -19,8 +19,18 @@ import ( type RoleAssignmentV3InitParameters struct { // The domain to assign the role in. + // +crossplane:generate:reference:type=github.com/crossplane-contrib/provider-openstack/apis/cluster/identity/v1alpha1.ProjectV3 + // +crossplane:generate:reference:extractor=github.com/crossplane-contrib/provider-openstack/apis/cluster/identity/v1alpha1.ExtractDomainID() DomainID *string `json:"domainId,omitempty" tf:"domain_id,omitempty"` + // Reference to a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDRef *v1.Reference `json:"domainIdRef,omitempty" tf:"-"` + + // Selector for a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDSelector *v1.Selector `json:"domainIdSelector,omitempty" tf:"-"` + // The group to assign the role to. GroupID *string `json:"groupId,omitempty" tf:"group_id,omitempty"` @@ -96,9 +106,19 @@ type RoleAssignmentV3Observation struct { type RoleAssignmentV3Parameters struct { // The domain to assign the role in. + // +crossplane:generate:reference:type=github.com/crossplane-contrib/provider-openstack/apis/cluster/identity/v1alpha1.ProjectV3 + // +crossplane:generate:reference:extractor=github.com/crossplane-contrib/provider-openstack/apis/cluster/identity/v1alpha1.ExtractDomainID() // +kubebuilder:validation:Optional DomainID *string `json:"domainId,omitempty" tf:"domain_id,omitempty"` + // Reference to a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDRef *v1.Reference `json:"domainIdRef,omitempty" tf:"-"` + + // Selector for a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDSelector *v1.Selector `json:"domainIdSelector,omitempty" tf:"-"` + // The group to assign the role to. // +kubebuilder:validation:Optional GroupID *string `json:"groupId,omitempty" tf:"group_id,omitempty"` diff --git a/apis/cluster/identity/v1alpha1/zz_rolev3_types.go b/apis/cluster/identity/v1alpha1/zz_rolev3_types.go index 4dbfad4f..90911b3b 100755 --- a/apis/cluster/identity/v1alpha1/zz_rolev3_types.go +++ b/apis/cluster/identity/v1alpha1/zz_rolev3_types.go @@ -19,8 +19,18 @@ import ( type RoleV3InitParameters struct { // The domain the role belongs to. + // +crossplane:generate:reference:type=github.com/crossplane-contrib/provider-openstack/apis/cluster/identity/v1alpha1.ProjectV3 + // +crossplane:generate:reference:extractor=github.com/crossplane-contrib/provider-openstack/apis/cluster/identity/v1alpha1.ExtractDomainID() DomainID *string `json:"domainId,omitempty" tf:"domain_id,omitempty"` + // Reference to a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDRef *v1.Reference `json:"domainIdRef,omitempty" tf:"-"` + + // Selector for a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDSelector *v1.Selector `json:"domainIdSelector,omitempty" tf:"-"` + // The name of the role. Name *string `json:"name,omitempty" tf:"name,omitempty"` @@ -49,9 +59,19 @@ type RoleV3Observation struct { type RoleV3Parameters struct { // The domain the role belongs to. + // +crossplane:generate:reference:type=github.com/crossplane-contrib/provider-openstack/apis/cluster/identity/v1alpha1.ProjectV3 + // +crossplane:generate:reference:extractor=github.com/crossplane-contrib/provider-openstack/apis/cluster/identity/v1alpha1.ExtractDomainID() // +kubebuilder:validation:Optional DomainID *string `json:"domainId,omitempty" tf:"domain_id,omitempty"` + // Reference to a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDRef *v1.Reference `json:"domainIdRef,omitempty" tf:"-"` + + // Selector for a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDSelector *v1.Selector `json:"domainIdSelector,omitempty" tf:"-"` + // The name of the role. // +kubebuilder:validation:Optional Name *string `json:"name,omitempty" tf:"name,omitempty"` diff --git a/apis/cluster/identity/v1alpha1/zz_userv3_types.go b/apis/cluster/identity/v1alpha1/zz_userv3_types.go index 85f41a6b..7252ea34 100755 --- a/apis/cluster/identity/v1alpha1/zz_userv3_types.go +++ b/apis/cluster/identity/v1alpha1/zz_userv3_types.go @@ -57,8 +57,18 @@ type UserV3InitParameters struct { Description *string `json:"description,omitempty" tf:"description,omitempty"` // The domain this user belongs to. + // +crossplane:generate:reference:type=github.com/crossplane-contrib/provider-openstack/apis/cluster/identity/v1alpha1.ProjectV3 + // +crossplane:generate:reference:extractor=github.com/crossplane-contrib/provider-openstack/apis/cluster/identity/v1alpha1.ExtractDomainID() DomainID *string `json:"domainId,omitempty" tf:"domain_id,omitempty"` + // Reference to a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDRef *v1.Reference `json:"domainIdRef,omitempty" tf:"-"` + + // Selector for a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDSelector *v1.Selector `json:"domainIdSelector,omitempty" tf:"-"` + // Whether the user is enabled or disabled. Valid // values are true and false. Enabled *bool `json:"enabled,omitempty" tf:"enabled,omitempty"` @@ -174,9 +184,19 @@ type UserV3Parameters struct { Description *string `json:"description,omitempty" tf:"description,omitempty"` // The domain this user belongs to. + // +crossplane:generate:reference:type=github.com/crossplane-contrib/provider-openstack/apis/cluster/identity/v1alpha1.ProjectV3 + // +crossplane:generate:reference:extractor=github.com/crossplane-contrib/provider-openstack/apis/cluster/identity/v1alpha1.ExtractDomainID() // +kubebuilder:validation:Optional DomainID *string `json:"domainId,omitempty" tf:"domain_id,omitempty"` + // Reference to a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDRef *v1.Reference `json:"domainIdRef,omitempty" tf:"-"` + + // Selector for a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDSelector *v1.Selector `json:"domainIdSelector,omitempty" tf:"-"` + // Whether the user is enabled or disabled. Valid // values are true and false. // +kubebuilder:validation:Optional diff --git a/apis/namespaced/identity/v1alpha1/projectv3_extractor.go b/apis/namespaced/identity/v1alpha1/projectv3_extractor.go new file mode 100644 index 00000000..eb6af7cb --- /dev/null +++ b/apis/namespaced/identity/v1alpha1/projectv3_extractor.go @@ -0,0 +1,30 @@ +/* +Copyright 2022 Upbound Inc. +Copyright 2023 Jakob Schlagenhaufer +Copyright 2025 Yannick Schlosser +*/ + +package v1alpha1 + +import ( + "github.com/crossplane/crossplane-runtime/v2/pkg/reference" + "github.com/crossplane/crossplane-runtime/v2/pkg/resource" +) + +// ExtractDomainID returns the external name of a ProjectV3 only when it +// represents a domain (isDomain: true). Referencing a regular project +// yields an empty value, causing reference resolution to fail. +func ExtractDomainID() reference.ExtractValueFn { + return func(mg resource.Managed) string { + p, ok := any(mg).(*ProjectV3) + if !ok { + return "" + } + isDomain := (p.Spec.ForProvider.IsDomain != nil && *p.Spec.ForProvider.IsDomain) || + (p.Status.AtProvider.IsDomain != nil && *p.Status.AtProvider.IsDomain) + if !isDomain { + return "" + } + return reference.ExternalName()(mg) + } +} diff --git a/apis/namespaced/identity/v1alpha1/zz_generated.deepcopy.go b/apis/namespaced/identity/v1alpha1/zz_generated.deepcopy.go index 6e7435d5..ae6b3e95 100644 --- a/apis/namespaced/identity/v1alpha1/zz_generated.deepcopy.go +++ b/apis/namespaced/identity/v1alpha1/zz_generated.deepcopy.go @@ -922,6 +922,16 @@ func (in *GroupV3InitParameters) DeepCopyInto(out *GroupV3InitParameters) { *out = new(string) **out = **in } + if in.DomainIDRef != nil { + in, out := &in.DomainIDRef, &out.DomainIDRef + *out = new(v1.NamespacedReference) + (*in).DeepCopyInto(*out) + } + if in.DomainIDSelector != nil { + in, out := &in.DomainIDSelector, &out.DomainIDSelector + *out = new(v1.NamespacedSelector) + (*in).DeepCopyInto(*out) + } if in.Name != nil { in, out := &in.Name, &out.Name *out = new(string) @@ -1029,6 +1039,16 @@ func (in *GroupV3Parameters) DeepCopyInto(out *GroupV3Parameters) { *out = new(string) **out = **in } + if in.DomainIDRef != nil { + in, out := &in.DomainIDRef, &out.DomainIDRef + *out = new(v1.NamespacedReference) + (*in).DeepCopyInto(*out) + } + if in.DomainIDSelector != nil { + in, out := &in.DomainIDSelector, &out.DomainIDSelector + *out = new(v1.NamespacedSelector) + (*in).DeepCopyInto(*out) + } if in.Name != nil { in, out := &in.Name, &out.Name *out = new(string) @@ -1121,6 +1141,16 @@ func (in *InheritRoleAssignmentV3InitParameters) DeepCopyInto(out *InheritRoleAs *out = new(string) **out = **in } + if in.DomainIDRef != nil { + in, out := &in.DomainIDRef, &out.DomainIDRef + *out = new(v1.NamespacedReference) + (*in).DeepCopyInto(*out) + } + if in.DomainIDSelector != nil { + in, out := &in.DomainIDSelector, &out.DomainIDSelector + *out = new(v1.NamespacedSelector) + (*in).DeepCopyInto(*out) + } if in.GroupID != nil { in, out := &in.GroupID, &out.GroupID *out = new(string) @@ -1268,6 +1298,16 @@ func (in *InheritRoleAssignmentV3Parameters) DeepCopyInto(out *InheritRoleAssign *out = new(string) **out = **in } + if in.DomainIDRef != nil { + in, out := &in.DomainIDRef, &out.DomainIDRef + *out = new(v1.NamespacedReference) + (*in).DeepCopyInto(*out) + } + if in.DomainIDSelector != nil { + in, out := &in.DomainIDSelector, &out.DomainIDSelector + *out = new(v1.NamespacedSelector) + (*in).DeepCopyInto(*out) + } if in.GroupID != nil { in, out := &in.GroupID, &out.GroupID *out = new(string) @@ -1478,6 +1518,16 @@ func (in *ProjectV3InitParameters) DeepCopyInto(out *ProjectV3InitParameters) { *out = new(string) **out = **in } + if in.DomainIDRef != nil { + in, out := &in.DomainIDRef, &out.DomainIDRef + *out = new(v1.NamespacedReference) + (*in).DeepCopyInto(*out) + } + if in.DomainIDSelector != nil { + in, out := &in.DomainIDSelector, &out.DomainIDSelector + *out = new(v1.NamespacedSelector) + (*in).DeepCopyInto(*out) + } if in.Enabled != nil { in, out := &in.Enabled, &out.Enabled *out = new(bool) @@ -1637,6 +1687,16 @@ func (in *ProjectV3Parameters) DeepCopyInto(out *ProjectV3Parameters) { *out = new(string) **out = **in } + if in.DomainIDRef != nil { + in, out := &in.DomainIDRef, &out.DomainIDRef + *out = new(v1.NamespacedReference) + (*in).DeepCopyInto(*out) + } + if in.DomainIDSelector != nil { + in, out := &in.DomainIDSelector, &out.DomainIDSelector + *out = new(v1.NamespacedSelector) + (*in).DeepCopyInto(*out) + } if in.Enabled != nil { in, out := &in.Enabled, &out.Enabled *out = new(bool) @@ -1755,6 +1815,16 @@ func (in *RoleAssignmentV3InitParameters) DeepCopyInto(out *RoleAssignmentV3Init *out = new(string) **out = **in } + if in.DomainIDRef != nil { + in, out := &in.DomainIDRef, &out.DomainIDRef + *out = new(v1.NamespacedReference) + (*in).DeepCopyInto(*out) + } + if in.DomainIDSelector != nil { + in, out := &in.DomainIDSelector, &out.DomainIDSelector + *out = new(v1.NamespacedSelector) + (*in).DeepCopyInto(*out) + } if in.GroupID != nil { in, out := &in.GroupID, &out.GroupID *out = new(string) @@ -1912,6 +1982,16 @@ func (in *RoleAssignmentV3Parameters) DeepCopyInto(out *RoleAssignmentV3Paramete *out = new(string) **out = **in } + if in.DomainIDRef != nil { + in, out := &in.DomainIDRef, &out.DomainIDRef + *out = new(v1.NamespacedReference) + (*in).DeepCopyInto(*out) + } + if in.DomainIDSelector != nil { + in, out := &in.DomainIDSelector, &out.DomainIDSelector + *out = new(v1.NamespacedSelector) + (*in).DeepCopyInto(*out) + } if in.GroupID != nil { in, out := &in.GroupID, &out.GroupID *out = new(string) @@ -2049,6 +2129,16 @@ func (in *RoleV3InitParameters) DeepCopyInto(out *RoleV3InitParameters) { *out = new(string) **out = **in } + if in.DomainIDRef != nil { + in, out := &in.DomainIDRef, &out.DomainIDRef + *out = new(v1.NamespacedReference) + (*in).DeepCopyInto(*out) + } + if in.DomainIDSelector != nil { + in, out := &in.DomainIDSelector, &out.DomainIDSelector + *out = new(v1.NamespacedSelector) + (*in).DeepCopyInto(*out) + } if in.Name != nil { in, out := &in.Name, &out.Name *out = new(string) @@ -2146,6 +2236,16 @@ func (in *RoleV3Parameters) DeepCopyInto(out *RoleV3Parameters) { *out = new(string) **out = **in } + if in.DomainIDRef != nil { + in, out := &in.DomainIDRef, &out.DomainIDRef + *out = new(v1.NamespacedReference) + (*in).DeepCopyInto(*out) + } + if in.DomainIDSelector != nil { + in, out := &in.DomainIDSelector, &out.DomainIDSelector + *out = new(v1.NamespacedSelector) + (*in).DeepCopyInto(*out) + } if in.Name != nil { in, out := &in.Name, &out.Name *out = new(string) @@ -2706,6 +2806,16 @@ func (in *UserV3InitParameters) DeepCopyInto(out *UserV3InitParameters) { *out = new(string) **out = **in } + if in.DomainIDRef != nil { + in, out := &in.DomainIDRef, &out.DomainIDRef + *out = new(v1.NamespacedReference) + (*in).DeepCopyInto(*out) + } + if in.DomainIDSelector != nil { + in, out := &in.DomainIDSelector, &out.DomainIDSelector + *out = new(v1.NamespacedSelector) + (*in).DeepCopyInto(*out) + } if in.Enabled != nil { in, out := &in.Enabled, &out.Enabled *out = new(bool) @@ -2934,6 +3044,16 @@ func (in *UserV3Parameters) DeepCopyInto(out *UserV3Parameters) { *out = new(string) **out = **in } + if in.DomainIDRef != nil { + in, out := &in.DomainIDRef, &out.DomainIDRef + *out = new(v1.NamespacedReference) + (*in).DeepCopyInto(*out) + } + if in.DomainIDSelector != nil { + in, out := &in.DomainIDSelector, &out.DomainIDSelector + *out = new(v1.NamespacedSelector) + (*in).DeepCopyInto(*out) + } if in.Enabled != nil { in, out := &in.Enabled, &out.Enabled *out = new(bool) diff --git a/apis/namespaced/identity/v1alpha1/zz_generated.resolvers.go b/apis/namespaced/identity/v1alpha1/zz_generated.resolvers.go index a1ae27bf..68f482e9 100644 --- a/apis/namespaced/identity/v1alpha1/zz_generated.resolvers.go +++ b/apis/namespaced/identity/v1alpha1/zz_generated.resolvers.go @@ -94,6 +94,50 @@ func (mg *EndpointV3) ResolveReferences(ctx context.Context, c client.Reader) er return nil } +// ResolveReferences of this GroupV3. +func (mg *GroupV3) ResolveReferences(ctx context.Context, c client.Reader) error { + r := reference.NewAPINamespacedResolver(c, mg) + + var rsp reference.NamespacedResolutionResponse + var err error + + rsp, err = r.Resolve(ctx, reference.NamespacedResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.ForProvider.DomainID), + Extract: ExtractDomainID(), + Namespace: mg.GetNamespace(), + Reference: mg.Spec.ForProvider.DomainIDRef, + Selector: mg.Spec.ForProvider.DomainIDSelector, + To: reference.To{ + List: &ProjectV3List{}, + Managed: &ProjectV3{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.ForProvider.DomainID") + } + mg.Spec.ForProvider.DomainID = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.ForProvider.DomainIDRef = rsp.ResolvedReference + + rsp, err = r.Resolve(ctx, reference.NamespacedResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.InitProvider.DomainID), + Extract: ExtractDomainID(), + Namespace: mg.GetNamespace(), + Reference: mg.Spec.InitProvider.DomainIDRef, + Selector: mg.Spec.InitProvider.DomainIDSelector, + To: reference.To{ + List: &ProjectV3List{}, + Managed: &ProjectV3{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.InitProvider.DomainID") + } + mg.Spec.InitProvider.DomainID = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.InitProvider.DomainIDRef = rsp.ResolvedReference + + return nil +} + // ResolveReferences of this InheritRoleAssignmentV3. func (mg *InheritRoleAssignmentV3) ResolveReferences(ctx context.Context, c client.Reader) error { r := reference.NewAPINamespacedResolver(c, mg) @@ -101,6 +145,23 @@ func (mg *InheritRoleAssignmentV3) ResolveReferences(ctx context.Context, c clie var rsp reference.NamespacedResolutionResponse var err error + rsp, err = r.Resolve(ctx, reference.NamespacedResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.ForProvider.DomainID), + Extract: ExtractDomainID(), + Namespace: mg.GetNamespace(), + Reference: mg.Spec.ForProvider.DomainIDRef, + Selector: mg.Spec.ForProvider.DomainIDSelector, + To: reference.To{ + List: &ProjectV3List{}, + Managed: &ProjectV3{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.ForProvider.DomainID") + } + mg.Spec.ForProvider.DomainID = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.ForProvider.DomainIDRef = rsp.ResolvedReference + rsp, err = r.Resolve(ctx, reference.NamespacedResolutionRequest{ CurrentValue: reference.FromPtrValue(mg.Spec.ForProvider.RoleID), Extract: resource.ExtractResourceID(), @@ -135,6 +196,23 @@ func (mg *InheritRoleAssignmentV3) ResolveReferences(ctx context.Context, c clie mg.Spec.ForProvider.UserID = reference.ToPtrValue(rsp.ResolvedValue) mg.Spec.ForProvider.UserIDRef = rsp.ResolvedReference + rsp, err = r.Resolve(ctx, reference.NamespacedResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.InitProvider.DomainID), + Extract: ExtractDomainID(), + Namespace: mg.GetNamespace(), + Reference: mg.Spec.InitProvider.DomainIDRef, + Selector: mg.Spec.InitProvider.DomainIDSelector, + To: reference.To{ + List: &ProjectV3List{}, + Managed: &ProjectV3{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.InitProvider.DomainID") + } + mg.Spec.InitProvider.DomainID = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.InitProvider.DomainIDRef = rsp.ResolvedReference + rsp, err = r.Resolve(ctx, reference.NamespacedResolutionRequest{ CurrentValue: reference.FromPtrValue(mg.Spec.InitProvider.RoleID), Extract: resource.ExtractResourceID(), @@ -172,6 +250,50 @@ func (mg *InheritRoleAssignmentV3) ResolveReferences(ctx context.Context, c clie return nil } +// ResolveReferences of this ProjectV3. +func (mg *ProjectV3) ResolveReferences(ctx context.Context, c client.Reader) error { + r := reference.NewAPINamespacedResolver(c, mg) + + var rsp reference.NamespacedResolutionResponse + var err error + + rsp, err = r.Resolve(ctx, reference.NamespacedResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.ForProvider.DomainID), + Extract: ExtractDomainID(), + Namespace: mg.GetNamespace(), + Reference: mg.Spec.ForProvider.DomainIDRef, + Selector: mg.Spec.ForProvider.DomainIDSelector, + To: reference.To{ + List: &ProjectV3List{}, + Managed: &ProjectV3{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.ForProvider.DomainID") + } + mg.Spec.ForProvider.DomainID = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.ForProvider.DomainIDRef = rsp.ResolvedReference + + rsp, err = r.Resolve(ctx, reference.NamespacedResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.InitProvider.DomainID), + Extract: ExtractDomainID(), + Namespace: mg.GetNamespace(), + Reference: mg.Spec.InitProvider.DomainIDRef, + Selector: mg.Spec.InitProvider.DomainIDSelector, + To: reference.To{ + List: &ProjectV3List{}, + Managed: &ProjectV3{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.InitProvider.DomainID") + } + mg.Spec.InitProvider.DomainID = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.InitProvider.DomainIDRef = rsp.ResolvedReference + + return nil +} + // ResolveReferences of this RoleAssignmentV3. func (mg *RoleAssignmentV3) ResolveReferences(ctx context.Context, c client.Reader) error { r := reference.NewAPINamespacedResolver(c, mg) @@ -179,6 +301,23 @@ func (mg *RoleAssignmentV3) ResolveReferences(ctx context.Context, c client.Read var rsp reference.NamespacedResolutionResponse var err error + rsp, err = r.Resolve(ctx, reference.NamespacedResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.ForProvider.DomainID), + Extract: ExtractDomainID(), + Namespace: mg.GetNamespace(), + Reference: mg.Spec.ForProvider.DomainIDRef, + Selector: mg.Spec.ForProvider.DomainIDSelector, + To: reference.To{ + List: &ProjectV3List{}, + Managed: &ProjectV3{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.ForProvider.DomainID") + } + mg.Spec.ForProvider.DomainID = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.ForProvider.DomainIDRef = rsp.ResolvedReference + rsp, err = r.Resolve(ctx, reference.NamespacedResolutionRequest{ CurrentValue: reference.FromPtrValue(mg.Spec.ForProvider.ProjectID), Extract: reference.ExternalName(), @@ -230,6 +369,23 @@ func (mg *RoleAssignmentV3) ResolveReferences(ctx context.Context, c client.Read mg.Spec.ForProvider.UserID = reference.ToPtrValue(rsp.ResolvedValue) mg.Spec.ForProvider.UserIDRef = rsp.ResolvedReference + rsp, err = r.Resolve(ctx, reference.NamespacedResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.InitProvider.DomainID), + Extract: ExtractDomainID(), + Namespace: mg.GetNamespace(), + Reference: mg.Spec.InitProvider.DomainIDRef, + Selector: mg.Spec.InitProvider.DomainIDSelector, + To: reference.To{ + List: &ProjectV3List{}, + Managed: &ProjectV3{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.InitProvider.DomainID") + } + mg.Spec.InitProvider.DomainID = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.InitProvider.DomainIDRef = rsp.ResolvedReference + rsp, err = r.Resolve(ctx, reference.NamespacedResolutionRequest{ CurrentValue: reference.FromPtrValue(mg.Spec.InitProvider.ProjectID), Extract: reference.ExternalName(), @@ -284,6 +440,50 @@ func (mg *RoleAssignmentV3) ResolveReferences(ctx context.Context, c client.Read return nil } +// ResolveReferences of this RoleV3. +func (mg *RoleV3) ResolveReferences(ctx context.Context, c client.Reader) error { + r := reference.NewAPINamespacedResolver(c, mg) + + var rsp reference.NamespacedResolutionResponse + var err error + + rsp, err = r.Resolve(ctx, reference.NamespacedResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.ForProvider.DomainID), + Extract: ExtractDomainID(), + Namespace: mg.GetNamespace(), + Reference: mg.Spec.ForProvider.DomainIDRef, + Selector: mg.Spec.ForProvider.DomainIDSelector, + To: reference.To{ + List: &ProjectV3List{}, + Managed: &ProjectV3{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.ForProvider.DomainID") + } + mg.Spec.ForProvider.DomainID = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.ForProvider.DomainIDRef = rsp.ResolvedReference + + rsp, err = r.Resolve(ctx, reference.NamespacedResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.InitProvider.DomainID), + Extract: ExtractDomainID(), + Namespace: mg.GetNamespace(), + Reference: mg.Spec.InitProvider.DomainIDRef, + Selector: mg.Spec.InitProvider.DomainIDSelector, + To: reference.To{ + List: &ProjectV3List{}, + Managed: &ProjectV3{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.InitProvider.DomainID") + } + mg.Spec.InitProvider.DomainID = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.InitProvider.DomainIDRef = rsp.ResolvedReference + + return nil +} + // ResolveReferences of this UserMembershipV3. func (mg *UserMembershipV3) ResolveReferences(ctx context.Context, c client.Reader) error { r := reference.NewAPINamespacedResolver(c, mg) @@ -386,6 +586,23 @@ func (mg *UserV3) ResolveReferences(ctx context.Context, c client.Reader) error mg.Spec.ForProvider.DefaultProjectID = reference.ToPtrValue(rsp.ResolvedValue) mg.Spec.ForProvider.DefaultProjectIDRef = rsp.ResolvedReference + rsp, err = r.Resolve(ctx, reference.NamespacedResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.ForProvider.DomainID), + Extract: ExtractDomainID(), + Namespace: mg.GetNamespace(), + Reference: mg.Spec.ForProvider.DomainIDRef, + Selector: mg.Spec.ForProvider.DomainIDSelector, + To: reference.To{ + List: &ProjectV3List{}, + Managed: &ProjectV3{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.ForProvider.DomainID") + } + mg.Spec.ForProvider.DomainID = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.ForProvider.DomainIDRef = rsp.ResolvedReference + rsp, err = r.Resolve(ctx, reference.NamespacedResolutionRequest{ CurrentValue: reference.FromPtrValue(mg.Spec.InitProvider.DefaultProjectID), Extract: resource.ExtractResourceID(), @@ -403,5 +620,22 @@ func (mg *UserV3) ResolveReferences(ctx context.Context, c client.Reader) error mg.Spec.InitProvider.DefaultProjectID = reference.ToPtrValue(rsp.ResolvedValue) mg.Spec.InitProvider.DefaultProjectIDRef = rsp.ResolvedReference + rsp, err = r.Resolve(ctx, reference.NamespacedResolutionRequest{ + CurrentValue: reference.FromPtrValue(mg.Spec.InitProvider.DomainID), + Extract: ExtractDomainID(), + Namespace: mg.GetNamespace(), + Reference: mg.Spec.InitProvider.DomainIDRef, + Selector: mg.Spec.InitProvider.DomainIDSelector, + To: reference.To{ + List: &ProjectV3List{}, + Managed: &ProjectV3{}, + }, + }) + if err != nil { + return errors.Wrap(err, "mg.Spec.InitProvider.DomainID") + } + mg.Spec.InitProvider.DomainID = reference.ToPtrValue(rsp.ResolvedValue) + mg.Spec.InitProvider.DomainIDRef = rsp.ResolvedReference + return nil } diff --git a/apis/namespaced/identity/v1alpha1/zz_groupv3_types.go b/apis/namespaced/identity/v1alpha1/zz_groupv3_types.go index 13fdba71..22dd625a 100755 --- a/apis/namespaced/identity/v1alpha1/zz_groupv3_types.go +++ b/apis/namespaced/identity/v1alpha1/zz_groupv3_types.go @@ -23,8 +23,18 @@ type GroupV3InitParameters struct { Description *string `json:"description,omitempty" tf:"description,omitempty"` // The domain the group belongs to. + // +crossplane:generate:reference:type=github.com/crossplane-contrib/provider-openstack/apis/namespaced/identity/v1alpha1.ProjectV3 + // +crossplane:generate:reference:extractor=github.com/crossplane-contrib/provider-openstack/apis/namespaced/identity/v1alpha1.ExtractDomainID() DomainID *string `json:"domainId,omitempty" tf:"domain_id,omitempty"` + // Reference to a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDRef *v1.NamespacedReference `json:"domainIdRef,omitempty" tf:"-"` + + // Selector for a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDSelector *v1.NamespacedSelector `json:"domainIdSelector,omitempty" tf:"-"` + // The name of the group. Name *string `json:"name,omitempty" tf:"name,omitempty"` @@ -60,9 +70,19 @@ type GroupV3Parameters struct { Description *string `json:"description,omitempty" tf:"description,omitempty"` // The domain the group belongs to. + // +crossplane:generate:reference:type=github.com/crossplane-contrib/provider-openstack/apis/namespaced/identity/v1alpha1.ProjectV3 + // +crossplane:generate:reference:extractor=github.com/crossplane-contrib/provider-openstack/apis/namespaced/identity/v1alpha1.ExtractDomainID() // +kubebuilder:validation:Optional DomainID *string `json:"domainId,omitempty" tf:"domain_id,omitempty"` + // Reference to a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDRef *v1.NamespacedReference `json:"domainIdRef,omitempty" tf:"-"` + + // Selector for a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDSelector *v1.NamespacedSelector `json:"domainIdSelector,omitempty" tf:"-"` + // The name of the group. // +kubebuilder:validation:Optional Name *string `json:"name,omitempty" tf:"name,omitempty"` diff --git a/apis/namespaced/identity/v1alpha1/zz_inheritroleassignmentv3_types.go b/apis/namespaced/identity/v1alpha1/zz_inheritroleassignmentv3_types.go index 8eb66789..8a742a85 100755 --- a/apis/namespaced/identity/v1alpha1/zz_inheritroleassignmentv3_types.go +++ b/apis/namespaced/identity/v1alpha1/zz_inheritroleassignmentv3_types.go @@ -20,8 +20,18 @@ import ( type InheritRoleAssignmentV3InitParameters struct { // The domain to assign the role in. + // +crossplane:generate:reference:type=github.com/crossplane-contrib/provider-openstack/apis/namespaced/identity/v1alpha1.ProjectV3 + // +crossplane:generate:reference:extractor=github.com/crossplane-contrib/provider-openstack/apis/namespaced/identity/v1alpha1.ExtractDomainID() DomainID *string `json:"domainId,omitempty" tf:"domain_id,omitempty"` + // Reference to a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDRef *v1.NamespacedReference `json:"domainIdRef,omitempty" tf:"-"` + + // Selector for a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDSelector *v1.NamespacedSelector `json:"domainIdSelector,omitempty" tf:"-"` + // The group to assign the role to. GroupID *string `json:"groupId,omitempty" tf:"group_id,omitempty"` @@ -90,9 +100,19 @@ type InheritRoleAssignmentV3Observation struct { type InheritRoleAssignmentV3Parameters struct { // The domain to assign the role in. + // +crossplane:generate:reference:type=github.com/crossplane-contrib/provider-openstack/apis/namespaced/identity/v1alpha1.ProjectV3 + // +crossplane:generate:reference:extractor=github.com/crossplane-contrib/provider-openstack/apis/namespaced/identity/v1alpha1.ExtractDomainID() // +kubebuilder:validation:Optional DomainID *string `json:"domainId,omitempty" tf:"domain_id,omitempty"` + // Reference to a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDRef *v1.NamespacedReference `json:"domainIdRef,omitempty" tf:"-"` + + // Selector for a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDSelector *v1.NamespacedSelector `json:"domainIdSelector,omitempty" tf:"-"` + // The group to assign the role to. // +kubebuilder:validation:Optional GroupID *string `json:"groupId,omitempty" tf:"group_id,omitempty"` diff --git a/apis/namespaced/identity/v1alpha1/zz_projectv3_types.go b/apis/namespaced/identity/v1alpha1/zz_projectv3_types.go index f33b425e..9f569f63 100755 --- a/apis/namespaced/identity/v1alpha1/zz_projectv3_types.go +++ b/apis/namespaced/identity/v1alpha1/zz_projectv3_types.go @@ -23,8 +23,18 @@ type ProjectV3InitParameters struct { Description *string `json:"description,omitempty" tf:"description,omitempty"` // The domain this project belongs to. + // +crossplane:generate:reference:type=github.com/crossplane-contrib/provider-openstack/apis/namespaced/identity/v1alpha1.ProjectV3 + // +crossplane:generate:reference:extractor=github.com/crossplane-contrib/provider-openstack/apis/namespaced/identity/v1alpha1.ExtractDomainID() DomainID *string `json:"domainId,omitempty" tf:"domain_id,omitempty"` + // Reference to a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDRef *v1.NamespacedReference `json:"domainIdRef,omitempty" tf:"-"` + + // Selector for a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDSelector *v1.NamespacedSelector `json:"domainIdSelector,omitempty" tf:"-"` + // Whether the project is enabled or disabled. Valid // values are true and false. Default is true. Enabled *bool `json:"enabled,omitempty" tf:"enabled,omitempty"` @@ -96,9 +106,19 @@ type ProjectV3Parameters struct { Description *string `json:"description,omitempty" tf:"description,omitempty"` // The domain this project belongs to. + // +crossplane:generate:reference:type=github.com/crossplane-contrib/provider-openstack/apis/namespaced/identity/v1alpha1.ProjectV3 + // +crossplane:generate:reference:extractor=github.com/crossplane-contrib/provider-openstack/apis/namespaced/identity/v1alpha1.ExtractDomainID() // +kubebuilder:validation:Optional DomainID *string `json:"domainId,omitempty" tf:"domain_id,omitempty"` + // Reference to a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDRef *v1.NamespacedReference `json:"domainIdRef,omitempty" tf:"-"` + + // Selector for a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDSelector *v1.NamespacedSelector `json:"domainIdSelector,omitempty" tf:"-"` + // Whether the project is enabled or disabled. Valid // values are true and false. Default is true. // +kubebuilder:validation:Optional diff --git a/apis/namespaced/identity/v1alpha1/zz_roleassignmentv3_types.go b/apis/namespaced/identity/v1alpha1/zz_roleassignmentv3_types.go index 39b0144e..39b8ea5b 100755 --- a/apis/namespaced/identity/v1alpha1/zz_roleassignmentv3_types.go +++ b/apis/namespaced/identity/v1alpha1/zz_roleassignmentv3_types.go @@ -20,8 +20,18 @@ import ( type RoleAssignmentV3InitParameters struct { // The domain to assign the role in. + // +crossplane:generate:reference:type=github.com/crossplane-contrib/provider-openstack/apis/namespaced/identity/v1alpha1.ProjectV3 + // +crossplane:generate:reference:extractor=github.com/crossplane-contrib/provider-openstack/apis/namespaced/identity/v1alpha1.ExtractDomainID() DomainID *string `json:"domainId,omitempty" tf:"domain_id,omitempty"` + // Reference to a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDRef *v1.NamespacedReference `json:"domainIdRef,omitempty" tf:"-"` + + // Selector for a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDSelector *v1.NamespacedSelector `json:"domainIdSelector,omitempty" tf:"-"` + // The group to assign the role to. GroupID *string `json:"groupId,omitempty" tf:"group_id,omitempty"` @@ -97,9 +107,19 @@ type RoleAssignmentV3Observation struct { type RoleAssignmentV3Parameters struct { // The domain to assign the role in. + // +crossplane:generate:reference:type=github.com/crossplane-contrib/provider-openstack/apis/namespaced/identity/v1alpha1.ProjectV3 + // +crossplane:generate:reference:extractor=github.com/crossplane-contrib/provider-openstack/apis/namespaced/identity/v1alpha1.ExtractDomainID() // +kubebuilder:validation:Optional DomainID *string `json:"domainId,omitempty" tf:"domain_id,omitempty"` + // Reference to a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDRef *v1.NamespacedReference `json:"domainIdRef,omitempty" tf:"-"` + + // Selector for a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDSelector *v1.NamespacedSelector `json:"domainIdSelector,omitempty" tf:"-"` + // The group to assign the role to. // +kubebuilder:validation:Optional GroupID *string `json:"groupId,omitempty" tf:"group_id,omitempty"` diff --git a/apis/namespaced/identity/v1alpha1/zz_rolev3_types.go b/apis/namespaced/identity/v1alpha1/zz_rolev3_types.go index 6ac67e5b..55fe9f84 100755 --- a/apis/namespaced/identity/v1alpha1/zz_rolev3_types.go +++ b/apis/namespaced/identity/v1alpha1/zz_rolev3_types.go @@ -20,8 +20,18 @@ import ( type RoleV3InitParameters struct { // The domain the role belongs to. + // +crossplane:generate:reference:type=github.com/crossplane-contrib/provider-openstack/apis/namespaced/identity/v1alpha1.ProjectV3 + // +crossplane:generate:reference:extractor=github.com/crossplane-contrib/provider-openstack/apis/namespaced/identity/v1alpha1.ExtractDomainID() DomainID *string `json:"domainId,omitempty" tf:"domain_id,omitempty"` + // Reference to a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDRef *v1.NamespacedReference `json:"domainIdRef,omitempty" tf:"-"` + + // Selector for a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDSelector *v1.NamespacedSelector `json:"domainIdSelector,omitempty" tf:"-"` + // The name of the role. Name *string `json:"name,omitempty" tf:"name,omitempty"` @@ -50,9 +60,19 @@ type RoleV3Observation struct { type RoleV3Parameters struct { // The domain the role belongs to. + // +crossplane:generate:reference:type=github.com/crossplane-contrib/provider-openstack/apis/namespaced/identity/v1alpha1.ProjectV3 + // +crossplane:generate:reference:extractor=github.com/crossplane-contrib/provider-openstack/apis/namespaced/identity/v1alpha1.ExtractDomainID() // +kubebuilder:validation:Optional DomainID *string `json:"domainId,omitempty" tf:"domain_id,omitempty"` + // Reference to a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDRef *v1.NamespacedReference `json:"domainIdRef,omitempty" tf:"-"` + + // Selector for a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDSelector *v1.NamespacedSelector `json:"domainIdSelector,omitempty" tf:"-"` + // The name of the role. // +kubebuilder:validation:Optional Name *string `json:"name,omitempty" tf:"name,omitempty"` diff --git a/apis/namespaced/identity/v1alpha1/zz_userv3_types.go b/apis/namespaced/identity/v1alpha1/zz_userv3_types.go index ed54de82..1af29317 100755 --- a/apis/namespaced/identity/v1alpha1/zz_userv3_types.go +++ b/apis/namespaced/identity/v1alpha1/zz_userv3_types.go @@ -58,8 +58,18 @@ type UserV3InitParameters struct { Description *string `json:"description,omitempty" tf:"description,omitempty"` // The domain this user belongs to. + // +crossplane:generate:reference:type=github.com/crossplane-contrib/provider-openstack/apis/namespaced/identity/v1alpha1.ProjectV3 + // +crossplane:generate:reference:extractor=github.com/crossplane-contrib/provider-openstack/apis/namespaced/identity/v1alpha1.ExtractDomainID() DomainID *string `json:"domainId,omitempty" tf:"domain_id,omitempty"` + // Reference to a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDRef *v1.NamespacedReference `json:"domainIdRef,omitempty" tf:"-"` + + // Selector for a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDSelector *v1.NamespacedSelector `json:"domainIdSelector,omitempty" tf:"-"` + // Whether the user is enabled or disabled. Valid // values are true and false. Enabled *bool `json:"enabled,omitempty" tf:"enabled,omitempty"` @@ -175,9 +185,19 @@ type UserV3Parameters struct { Description *string `json:"description,omitempty" tf:"description,omitempty"` // The domain this user belongs to. + // +crossplane:generate:reference:type=github.com/crossplane-contrib/provider-openstack/apis/namespaced/identity/v1alpha1.ProjectV3 + // +crossplane:generate:reference:extractor=github.com/crossplane-contrib/provider-openstack/apis/namespaced/identity/v1alpha1.ExtractDomainID() // +kubebuilder:validation:Optional DomainID *string `json:"domainId,omitempty" tf:"domain_id,omitempty"` + // Reference to a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDRef *v1.NamespacedReference `json:"domainIdRef,omitempty" tf:"-"` + + // Selector for a ProjectV3 in identity to populate domainId. + // +kubebuilder:validation:Optional + DomainIDSelector *v1.NamespacedSelector `json:"domainIdSelector,omitempty" tf:"-"` + // Whether the user is enabled or disabled. Valid // values are true and false. // +kubebuilder:validation:Optional diff --git a/config/cluster/identity/config.go b/config/cluster/identity/config.go index 02854b17..e4fcbeaf 100644 --- a/config/cluster/identity/config.go +++ b/config/cluster/identity/config.go @@ -1,12 +1,58 @@ package identity -import "github.com/crossplane/upjet/v2/pkg/config" +import ( + "github.com/crossplane/upjet/v2/pkg/config" + + "github.com/crossplane-contrib/provider-openstack/internal/initializer/domain" +) + +const domainIDExtractor = "github.com/crossplane-contrib/provider-openstack/apis/cluster/identity/v1alpha1.ExtractDomainID()" // Configure configures individual resources by adding custom ResourceConfigurators. func Configure(p *config.Provider) { + p.AddResourceConfigurator("openstack_identity_project_v3", func(r *config.Resource) { + r.References["domain_id"] = config.Reference{ + TerraformName: "openstack_identity_project_v3", + Extractor: domainIDExtractor, + } + r.InitializerFns = append(r.InitializerFns, domain.NewValidator) + }) + p.AddResourceConfigurator("openstack_identity_user_v3", func(r *config.Resource) { + r.References["domain_id"] = config.Reference{ + TerraformName: "openstack_identity_project_v3", + Extractor: domainIDExtractor, + } + r.InitializerFns = append(r.InitializerFns, domain.NewValidator) + }) + p.AddResourceConfigurator("openstack_identity_group_v3", func(r *config.Resource) { + r.References["domain_id"] = config.Reference{ + TerraformName: "openstack_identity_project_v3", + Extractor: domainIDExtractor, + } + r.InitializerFns = append(r.InitializerFns, domain.NewValidator) + }) + p.AddResourceConfigurator("openstack_identity_role_v3", func(r *config.Resource) { + r.References["domain_id"] = config.Reference{ + TerraformName: "openstack_identity_project_v3", + Extractor: domainIDExtractor, + } + r.InitializerFns = append(r.InitializerFns, domain.NewValidator) + }) p.AddResourceConfigurator("openstack_identity_role_assignment_v3", func(r *config.Resource) { r.References["project_id"] = config.Reference{ TerraformName: "openstack_identity_project_v3", } + r.References["domain_id"] = config.Reference{ + TerraformName: "openstack_identity_project_v3", + Extractor: domainIDExtractor, + } + r.InitializerFns = append(r.InitializerFns, domain.NewValidator) + }) + p.AddResourceConfigurator("openstack_identity_inherit_role_assignment_v3", func(r *config.Resource) { + r.References["domain_id"] = config.Reference{ + TerraformName: "openstack_identity_project_v3", + Extractor: domainIDExtractor, + } + r.InitializerFns = append(r.InitializerFns, domain.NewValidator) }) } diff --git a/config/namespaced/identity/config.go b/config/namespaced/identity/config.go index 02854b17..5182e87b 100644 --- a/config/namespaced/identity/config.go +++ b/config/namespaced/identity/config.go @@ -1,12 +1,58 @@ package identity -import "github.com/crossplane/upjet/v2/pkg/config" +import ( + "github.com/crossplane/upjet/v2/pkg/config" + + "github.com/crossplane-contrib/provider-openstack/internal/initializer/domain" +) + +const domainIDExtractor = "github.com/crossplane-contrib/provider-openstack/apis/namespaced/identity/v1alpha1.ExtractDomainID()" // Configure configures individual resources by adding custom ResourceConfigurators. func Configure(p *config.Provider) { + p.AddResourceConfigurator("openstack_identity_project_v3", func(r *config.Resource) { + r.References["domain_id"] = config.Reference{ + TerraformName: "openstack_identity_project_v3", + Extractor: domainIDExtractor, + } + r.InitializerFns = append(r.InitializerFns, domain.NewValidator) + }) + p.AddResourceConfigurator("openstack_identity_user_v3", func(r *config.Resource) { + r.References["domain_id"] = config.Reference{ + TerraformName: "openstack_identity_project_v3", + Extractor: domainIDExtractor, + } + r.InitializerFns = append(r.InitializerFns, domain.NewValidator) + }) + p.AddResourceConfigurator("openstack_identity_group_v3", func(r *config.Resource) { + r.References["domain_id"] = config.Reference{ + TerraformName: "openstack_identity_project_v3", + Extractor: domainIDExtractor, + } + r.InitializerFns = append(r.InitializerFns, domain.NewValidator) + }) + p.AddResourceConfigurator("openstack_identity_role_v3", func(r *config.Resource) { + r.References["domain_id"] = config.Reference{ + TerraformName: "openstack_identity_project_v3", + Extractor: domainIDExtractor, + } + r.InitializerFns = append(r.InitializerFns, domain.NewValidator) + }) p.AddResourceConfigurator("openstack_identity_role_assignment_v3", func(r *config.Resource) { r.References["project_id"] = config.Reference{ TerraformName: "openstack_identity_project_v3", } + r.References["domain_id"] = config.Reference{ + TerraformName: "openstack_identity_project_v3", + Extractor: domainIDExtractor, + } + r.InitializerFns = append(r.InitializerFns, domain.NewValidator) + }) + p.AddResourceConfigurator("openstack_identity_inherit_role_assignment_v3", func(r *config.Resource) { + r.References["domain_id"] = config.Reference{ + TerraformName: "openstack_identity_project_v3", + Extractor: domainIDExtractor, + } + r.InitializerFns = append(r.InitializerFns, domain.NewValidator) }) } diff --git a/examples-generated/cluster/identity/v1alpha1/inheritroleassignmentv3.yaml b/examples-generated/cluster/identity/v1alpha1/inheritroleassignmentv3.yaml index c0b3de5e..2854afe2 100644 --- a/examples-generated/cluster/identity/v1alpha1/inheritroleassignmentv3.yaml +++ b/examples-generated/cluster/identity/v1alpha1/inheritroleassignmentv3.yaml @@ -8,7 +8,9 @@ metadata: name: role-assignment-1 spec: forProvider: - domainId: default + domainIdSelector: + matchLabels: + testing.upbound.io/example-name: example roleIdSelector: matchLabels: testing.upbound.io/example-name: role_1 @@ -28,7 +30,9 @@ metadata: name: role-1 spec: forProvider: - domainId: default + domainIdSelector: + matchLabels: + testing.upbound.io/example-name: example name: role_1 --- @@ -43,5 +47,7 @@ metadata: name: user-1 spec: forProvider: - domainId: default + domainIdSelector: + matchLabels: + testing.upbound.io/example-name: example name: user_1 diff --git a/examples-generated/namespaced/identity/v1alpha1/inheritroleassignmentv3.yaml b/examples-generated/namespaced/identity/v1alpha1/inheritroleassignmentv3.yaml index 5dcedbc9..5f0f9940 100644 --- a/examples-generated/namespaced/identity/v1alpha1/inheritroleassignmentv3.yaml +++ b/examples-generated/namespaced/identity/v1alpha1/inheritroleassignmentv3.yaml @@ -9,7 +9,9 @@ metadata: namespace: upbound-system spec: forProvider: - domainId: default + domainIdSelector: + matchLabels: + testing.upbound.io/example-name: example roleIdSelector: matchLabels: testing.upbound.io/example-name: role_1 @@ -30,7 +32,9 @@ metadata: namespace: upbound-system spec: forProvider: - domainId: default + domainIdSelector: + matchLabels: + testing.upbound.io/example-name: example name: role_1 --- @@ -46,5 +50,7 @@ metadata: namespace: upbound-system spec: forProvider: - domainId: default + domainIdSelector: + matchLabels: + testing.upbound.io/example-name: example name: user_1 diff --git a/examples/cluster/identity/domain-ref-examples.yaml b/examples/cluster/identity/domain-ref-examples.yaml new file mode 100644 index 00000000..22e09812 --- /dev/null +++ b/examples/cluster/identity/domain-ref-examples.yaml @@ -0,0 +1,113 @@ +--- +# Step 1: Create a Domain (ProjectV3 with isDomain: true) +apiVersion: identity.openstack.crossplane.io/v1alpha1 +kind: ProjectV3 +metadata: + name: example-domain +spec: + forProvider: + isDomain: true + name: example-domain + description: "Domain created by Crossplane" + tags: + - "crossplane" + providerConfigRef: + name: openstack-admin-provider-config + +--- +# Step 2: Create a Project in that Domain using domainIdRef +apiVersion: identity.openstack.crossplane.io/v1alpha1 +kind: ProjectV3 +metadata: + name: project-in-domain +spec: + forProvider: + name: project-in-domain + description: "Project inside example-domain" + domainIdRef: + name: example-domain + tags: + - "crossplane" + providerConfigRef: + name: openstack-admin-provider-config + +--- +# Step 3: Create a User in that Domain using domainIdRef +apiVersion: identity.openstack.crossplane.io/v1alpha1 +kind: UserV3 +metadata: + name: user-in-domain +spec: + forProvider: + name: user-in-domain + domainIdRef: + name: example-domain + passwordSecretRef: + key: password + name: user-password-secret + namespace: crossplane-system + providerConfigRef: + name: openstack-admin-provider-config + +--- +# Step 4: Create a Group in that Domain using domainIdRef +apiVersion: identity.openstack.crossplane.io/v1alpha1 +kind: GroupV3 +metadata: + name: group-in-domain +spec: + forProvider: + name: group-in-domain + description: "Group inside example-domain" + domainIdRef: + name: example-domain + providerConfigRef: + name: openstack-admin-provider-config + +--- +# Step 5: Create a Role scoped to that Domain using domainIdRef +apiVersion: identity.openstack.crossplane.io/v1alpha1 +kind: RoleV3 +metadata: + name: role-in-domain +spec: + forProvider: + name: role-in-domain + domainIdRef: + name: example-domain + providerConfigRef: + name: openstack-admin-provider-config + +--- +# Step 6: Assign a role at domain level using domainIdRef +apiVersion: identity.openstack.crossplane.io/v1alpha1 +kind: RoleAssignmentV3 +metadata: + name: domain-role-assignment +spec: + forProvider: + domainIdRef: + name: example-domain + userIdRef: + name: user-in-domain + roleIdRef: + name: role-in-domain + providerConfigRef: + name: openstack-admin-provider-config + +--- +# Step 7: Inherit role assignment at domain level using domainIdRef +apiVersion: identity.openstack.crossplane.io/v1alpha1 +kind: InheritRoleAssignmentV3 +metadata: + name: domain-inherit-role-assignment +spec: + forProvider: + domainIdRef: + name: example-domain + userIdRef: + name: user-in-domain + roleIdRef: + name: role-in-domain + providerConfigRef: + name: openstack-admin-provider-config diff --git a/internal/controller/cluster/identity/groupv3/zz_controller.go b/internal/controller/cluster/identity/groupv3/zz_controller.go index b03a296c..2daf5ed5 100755 --- a/internal/controller/cluster/identity/groupv3/zz_controller.go +++ b/internal/controller/cluster/identity/groupv3/zz_controller.go @@ -42,6 +42,9 @@ func SetupGated(mgr ctrl.Manager, o tjcontroller.Options) error { func Setup(mgr ctrl.Manager, o tjcontroller.Options) error { name := managed.ControllerName(v1alpha1.GroupV3_GroupVersionKind.String()) var initializers managed.InitializerChain + for _, i := range o.Provider.Resources["openstack_identity_group_v3"].InitializerFns { + initializers = append(initializers, i(mgr.GetClient())) + } eventHandler := handler.NewEventHandler(handler.WithLogger(o.Logger.WithValues("gvk", v1alpha1.GroupV3_GroupVersionKind))) ac := tjcontroller.NewAPICallbacks(mgr, xpresource.ManagedKind(v1alpha1.GroupV3_GroupVersionKind), tjcontroller.WithEventHandler(eventHandler), tjcontroller.WithStatusUpdates(false)) opts := []managed.ReconcilerOption{ diff --git a/internal/controller/cluster/identity/inheritroleassignmentv3/zz_controller.go b/internal/controller/cluster/identity/inheritroleassignmentv3/zz_controller.go index 9f03c42d..12dbdfc8 100755 --- a/internal/controller/cluster/identity/inheritroleassignmentv3/zz_controller.go +++ b/internal/controller/cluster/identity/inheritroleassignmentv3/zz_controller.go @@ -42,6 +42,9 @@ func SetupGated(mgr ctrl.Manager, o tjcontroller.Options) error { func Setup(mgr ctrl.Manager, o tjcontroller.Options) error { name := managed.ControllerName(v1alpha1.InheritRoleAssignmentV3_GroupVersionKind.String()) var initializers managed.InitializerChain + for _, i := range o.Provider.Resources["openstack_identity_inherit_role_assignment_v3"].InitializerFns { + initializers = append(initializers, i(mgr.GetClient())) + } eventHandler := handler.NewEventHandler(handler.WithLogger(o.Logger.WithValues("gvk", v1alpha1.InheritRoleAssignmentV3_GroupVersionKind))) ac := tjcontroller.NewAPICallbacks(mgr, xpresource.ManagedKind(v1alpha1.InheritRoleAssignmentV3_GroupVersionKind), tjcontroller.WithEventHandler(eventHandler), tjcontroller.WithStatusUpdates(false)) opts := []managed.ReconcilerOption{ diff --git a/internal/controller/cluster/identity/projectv3/zz_controller.go b/internal/controller/cluster/identity/projectv3/zz_controller.go index d4ed6b14..ef222f0a 100755 --- a/internal/controller/cluster/identity/projectv3/zz_controller.go +++ b/internal/controller/cluster/identity/projectv3/zz_controller.go @@ -42,6 +42,9 @@ func SetupGated(mgr ctrl.Manager, o tjcontroller.Options) error { func Setup(mgr ctrl.Manager, o tjcontroller.Options) error { name := managed.ControllerName(v1alpha1.ProjectV3_GroupVersionKind.String()) var initializers managed.InitializerChain + for _, i := range o.Provider.Resources["openstack_identity_project_v3"].InitializerFns { + initializers = append(initializers, i(mgr.GetClient())) + } eventHandler := handler.NewEventHandler(handler.WithLogger(o.Logger.WithValues("gvk", v1alpha1.ProjectV3_GroupVersionKind))) ac := tjcontroller.NewAPICallbacks(mgr, xpresource.ManagedKind(v1alpha1.ProjectV3_GroupVersionKind), tjcontroller.WithEventHandler(eventHandler), tjcontroller.WithStatusUpdates(false)) opts := []managed.ReconcilerOption{ diff --git a/internal/controller/cluster/identity/roleassignmentv3/zz_controller.go b/internal/controller/cluster/identity/roleassignmentv3/zz_controller.go index 3296b0f8..65732df4 100755 --- a/internal/controller/cluster/identity/roleassignmentv3/zz_controller.go +++ b/internal/controller/cluster/identity/roleassignmentv3/zz_controller.go @@ -42,6 +42,9 @@ func SetupGated(mgr ctrl.Manager, o tjcontroller.Options) error { func Setup(mgr ctrl.Manager, o tjcontroller.Options) error { name := managed.ControllerName(v1alpha1.RoleAssignmentV3_GroupVersionKind.String()) var initializers managed.InitializerChain + for _, i := range o.Provider.Resources["openstack_identity_role_assignment_v3"].InitializerFns { + initializers = append(initializers, i(mgr.GetClient())) + } eventHandler := handler.NewEventHandler(handler.WithLogger(o.Logger.WithValues("gvk", v1alpha1.RoleAssignmentV3_GroupVersionKind))) ac := tjcontroller.NewAPICallbacks(mgr, xpresource.ManagedKind(v1alpha1.RoleAssignmentV3_GroupVersionKind), tjcontroller.WithEventHandler(eventHandler), tjcontroller.WithStatusUpdates(false)) opts := []managed.ReconcilerOption{ diff --git a/internal/controller/cluster/identity/rolev3/zz_controller.go b/internal/controller/cluster/identity/rolev3/zz_controller.go index c162d6f8..4485a26e 100755 --- a/internal/controller/cluster/identity/rolev3/zz_controller.go +++ b/internal/controller/cluster/identity/rolev3/zz_controller.go @@ -42,6 +42,9 @@ func SetupGated(mgr ctrl.Manager, o tjcontroller.Options) error { func Setup(mgr ctrl.Manager, o tjcontroller.Options) error { name := managed.ControllerName(v1alpha1.RoleV3_GroupVersionKind.String()) var initializers managed.InitializerChain + for _, i := range o.Provider.Resources["openstack_identity_role_v3"].InitializerFns { + initializers = append(initializers, i(mgr.GetClient())) + } eventHandler := handler.NewEventHandler(handler.WithLogger(o.Logger.WithValues("gvk", v1alpha1.RoleV3_GroupVersionKind))) ac := tjcontroller.NewAPICallbacks(mgr, xpresource.ManagedKind(v1alpha1.RoleV3_GroupVersionKind), tjcontroller.WithEventHandler(eventHandler), tjcontroller.WithStatusUpdates(false)) opts := []managed.ReconcilerOption{ diff --git a/internal/controller/cluster/identity/userv3/zz_controller.go b/internal/controller/cluster/identity/userv3/zz_controller.go index 5138619b..8d4f9d74 100755 --- a/internal/controller/cluster/identity/userv3/zz_controller.go +++ b/internal/controller/cluster/identity/userv3/zz_controller.go @@ -42,6 +42,9 @@ func SetupGated(mgr ctrl.Manager, o tjcontroller.Options) error { func Setup(mgr ctrl.Manager, o tjcontroller.Options) error { name := managed.ControllerName(v1alpha1.UserV3_GroupVersionKind.String()) var initializers managed.InitializerChain + for _, i := range o.Provider.Resources["openstack_identity_user_v3"].InitializerFns { + initializers = append(initializers, i(mgr.GetClient())) + } eventHandler := handler.NewEventHandler(handler.WithLogger(o.Logger.WithValues("gvk", v1alpha1.UserV3_GroupVersionKind))) ac := tjcontroller.NewAPICallbacks(mgr, xpresource.ManagedKind(v1alpha1.UserV3_GroupVersionKind), tjcontroller.WithEventHandler(eventHandler), tjcontroller.WithStatusUpdates(false)) opts := []managed.ReconcilerOption{ diff --git a/internal/controller/namespaced/identity/groupv3/zz_controller.go b/internal/controller/namespaced/identity/groupv3/zz_controller.go index d693c81c..85e06f1e 100755 --- a/internal/controller/namespaced/identity/groupv3/zz_controller.go +++ b/internal/controller/namespaced/identity/groupv3/zz_controller.go @@ -42,6 +42,9 @@ func SetupGated(mgr ctrl.Manager, o tjcontroller.Options) error { func Setup(mgr ctrl.Manager, o tjcontroller.Options) error { name := managed.ControllerName(v1alpha1.GroupV3_GroupVersionKind.String()) var initializers managed.InitializerChain + for _, i := range o.Provider.Resources["openstack_identity_group_v3"].InitializerFns { + initializers = append(initializers, i(mgr.GetClient())) + } eventHandler := handler.NewEventHandler(handler.WithLogger(o.Logger.WithValues("gvk", v1alpha1.GroupV3_GroupVersionKind))) ac := tjcontroller.NewAPICallbacks(mgr, xpresource.ManagedKind(v1alpha1.GroupV3_GroupVersionKind), tjcontroller.WithEventHandler(eventHandler), tjcontroller.WithStatusUpdates(false)) opts := []managed.ReconcilerOption{ diff --git a/internal/controller/namespaced/identity/inheritroleassignmentv3/zz_controller.go b/internal/controller/namespaced/identity/inheritroleassignmentv3/zz_controller.go index d3b3fb88..43ad9136 100755 --- a/internal/controller/namespaced/identity/inheritroleassignmentv3/zz_controller.go +++ b/internal/controller/namespaced/identity/inheritroleassignmentv3/zz_controller.go @@ -42,6 +42,9 @@ func SetupGated(mgr ctrl.Manager, o tjcontroller.Options) error { func Setup(mgr ctrl.Manager, o tjcontroller.Options) error { name := managed.ControllerName(v1alpha1.InheritRoleAssignmentV3_GroupVersionKind.String()) var initializers managed.InitializerChain + for _, i := range o.Provider.Resources["openstack_identity_inherit_role_assignment_v3"].InitializerFns { + initializers = append(initializers, i(mgr.GetClient())) + } eventHandler := handler.NewEventHandler(handler.WithLogger(o.Logger.WithValues("gvk", v1alpha1.InheritRoleAssignmentV3_GroupVersionKind))) ac := tjcontroller.NewAPICallbacks(mgr, xpresource.ManagedKind(v1alpha1.InheritRoleAssignmentV3_GroupVersionKind), tjcontroller.WithEventHandler(eventHandler), tjcontroller.WithStatusUpdates(false)) opts := []managed.ReconcilerOption{ diff --git a/internal/controller/namespaced/identity/projectv3/zz_controller.go b/internal/controller/namespaced/identity/projectv3/zz_controller.go index 4e60e805..4bfb3456 100755 --- a/internal/controller/namespaced/identity/projectv3/zz_controller.go +++ b/internal/controller/namespaced/identity/projectv3/zz_controller.go @@ -42,6 +42,9 @@ func SetupGated(mgr ctrl.Manager, o tjcontroller.Options) error { func Setup(mgr ctrl.Manager, o tjcontroller.Options) error { name := managed.ControllerName(v1alpha1.ProjectV3_GroupVersionKind.String()) var initializers managed.InitializerChain + for _, i := range o.Provider.Resources["openstack_identity_project_v3"].InitializerFns { + initializers = append(initializers, i(mgr.GetClient())) + } eventHandler := handler.NewEventHandler(handler.WithLogger(o.Logger.WithValues("gvk", v1alpha1.ProjectV3_GroupVersionKind))) ac := tjcontroller.NewAPICallbacks(mgr, xpresource.ManagedKind(v1alpha1.ProjectV3_GroupVersionKind), tjcontroller.WithEventHandler(eventHandler), tjcontroller.WithStatusUpdates(false)) opts := []managed.ReconcilerOption{ diff --git a/internal/controller/namespaced/identity/roleassignmentv3/zz_controller.go b/internal/controller/namespaced/identity/roleassignmentv3/zz_controller.go index bed20474..5913160c 100755 --- a/internal/controller/namespaced/identity/roleassignmentv3/zz_controller.go +++ b/internal/controller/namespaced/identity/roleassignmentv3/zz_controller.go @@ -42,6 +42,9 @@ func SetupGated(mgr ctrl.Manager, o tjcontroller.Options) error { func Setup(mgr ctrl.Manager, o tjcontroller.Options) error { name := managed.ControllerName(v1alpha1.RoleAssignmentV3_GroupVersionKind.String()) var initializers managed.InitializerChain + for _, i := range o.Provider.Resources["openstack_identity_role_assignment_v3"].InitializerFns { + initializers = append(initializers, i(mgr.GetClient())) + } eventHandler := handler.NewEventHandler(handler.WithLogger(o.Logger.WithValues("gvk", v1alpha1.RoleAssignmentV3_GroupVersionKind))) ac := tjcontroller.NewAPICallbacks(mgr, xpresource.ManagedKind(v1alpha1.RoleAssignmentV3_GroupVersionKind), tjcontroller.WithEventHandler(eventHandler), tjcontroller.WithStatusUpdates(false)) opts := []managed.ReconcilerOption{ diff --git a/internal/controller/namespaced/identity/rolev3/zz_controller.go b/internal/controller/namespaced/identity/rolev3/zz_controller.go index bc05959f..c363f7b8 100755 --- a/internal/controller/namespaced/identity/rolev3/zz_controller.go +++ b/internal/controller/namespaced/identity/rolev3/zz_controller.go @@ -42,6 +42,9 @@ func SetupGated(mgr ctrl.Manager, o tjcontroller.Options) error { func Setup(mgr ctrl.Manager, o tjcontroller.Options) error { name := managed.ControllerName(v1alpha1.RoleV3_GroupVersionKind.String()) var initializers managed.InitializerChain + for _, i := range o.Provider.Resources["openstack_identity_role_v3"].InitializerFns { + initializers = append(initializers, i(mgr.GetClient())) + } eventHandler := handler.NewEventHandler(handler.WithLogger(o.Logger.WithValues("gvk", v1alpha1.RoleV3_GroupVersionKind))) ac := tjcontroller.NewAPICallbacks(mgr, xpresource.ManagedKind(v1alpha1.RoleV3_GroupVersionKind), tjcontroller.WithEventHandler(eventHandler), tjcontroller.WithStatusUpdates(false)) opts := []managed.ReconcilerOption{ diff --git a/internal/controller/namespaced/identity/userv3/zz_controller.go b/internal/controller/namespaced/identity/userv3/zz_controller.go index ae46848e..cca765a5 100755 --- a/internal/controller/namespaced/identity/userv3/zz_controller.go +++ b/internal/controller/namespaced/identity/userv3/zz_controller.go @@ -42,6 +42,9 @@ func SetupGated(mgr ctrl.Manager, o tjcontroller.Options) error { func Setup(mgr ctrl.Manager, o tjcontroller.Options) error { name := managed.ControllerName(v1alpha1.UserV3_GroupVersionKind.String()) var initializers managed.InitializerChain + for _, i := range o.Provider.Resources["openstack_identity_user_v3"].InitializerFns { + initializers = append(initializers, i(mgr.GetClient())) + } eventHandler := handler.NewEventHandler(handler.WithLogger(o.Logger.WithValues("gvk", v1alpha1.UserV3_GroupVersionKind))) ac := tjcontroller.NewAPICallbacks(mgr, xpresource.ManagedKind(v1alpha1.UserV3_GroupVersionKind), tjcontroller.WithEventHandler(eventHandler), tjcontroller.WithStatusUpdates(false)) opts := []managed.ReconcilerOption{ diff --git a/internal/initializer/domain/domain.go b/internal/initializer/domain/domain.go new file mode 100644 index 00000000..183eac24 --- /dev/null +++ b/internal/initializer/domain/domain.go @@ -0,0 +1,191 @@ +/* +Copyright 2026 The Crossplane Authors. +*/ + +// Package domain provides a managed resource initializer that reports a clear +// error when a domainId reference points to a ProjectV3 that is not a domain. +package domain + +import ( + "context" + "fmt" + + apimeta "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/crossplane/crossplane-runtime/v2/pkg/fieldpath" + "github.com/crossplane/crossplane-runtime/v2/pkg/meta" + "github.com/crossplane/crossplane-runtime/v2/pkg/reconciler/managed" + "github.com/crossplane/crossplane-runtime/v2/pkg/resource" +) + +const projectKind = "ProjectV3" + +// isDomainPaths are the fields consulted to decide whether a ProjectV3 is a +// domain. They must stay in sync with the ExtractDomainID reference extractors +// in apis/{cluster,namespaced}/identity/v1alpha1, otherwise this initializer +// would accept a reference that the resolver later rejects. +var isDomainPaths = []string{"spec.forProvider.isDomain", "status.atProvider.isDomain"} + +// sites are the parameter blocks that can carry a domainId reference. +var sites = []string{"spec.forProvider", "spec.initProvider"} + +// NewValidator returns an Initializer that verifies domainId references point +// to a ProjectV3 that is a domain. It satisfies upjet's config.NewInitializerFn. +func NewValidator(c client.Client) managed.Initializer { + return &validator{client: c} +} + +type validator struct { + client client.Client +} + +// Initialize returns an error describing the problem when a domainId reference +// resolves to a ProjectV3 that is not a domain. Every other situation, in +// particular a reference to an object that does not exist yet, is left to the +// regular reference resolution that runs after initialization. +func (v *validator) Initialize(ctx context.Context, mg resource.Managed) error { + gvk, err := v.client.GroupVersionKindFor(mg) + if err != nil { + return nil //nolint:nilerr // Not our concern; the reconciler reports it. + } + + paved, err := fieldpath.PaveObject(mg) + if err != nil { + return nil //nolint:nilerr // Not our concern; the reconciler reports it. + } + + for _, site := range sites { + // A resolved value is cached in the spec, so there is nothing left to + // check and we avoid re-reading the target on every reconcile. + if id, err := paved.GetString(site + ".domainId"); err == nil && id != "" { + continue + } + if err := v.checkSite(ctx, mg, paved, gvk.GroupVersion(), site); err != nil { + return err + } + } + + return nil +} + +func (v *validator) checkSite(ctx context.Context, mg resource.Managed, paved *fieldpath.Paved, gv schema.GroupVersion, site string) error { + if err := v.checkRef(ctx, mg, paved, gv, site); err != nil { + return err + } + return v.checkSelector(ctx, mg, paved, gv, site) +} + +func (v *validator) checkRef(ctx context.Context, mg resource.Managed, paved *fieldpath.Paved, gv schema.GroupVersion, site string) error { + name, err := paved.GetString(site + ".domainIdRef.name") + if err != nil { + return nil //nolint:nilerr // Field not present; nothing to validate. + } + if name == "" { + return nil + } + ns := refNamespace(paved, site+".domainIdRef.namespace", mg) + target, err := v.getProject(ctx, gv, types.NamespacedName{Name: name, Namespace: ns}) + if err != nil || target == nil { + return nil //nolint:nilerr // Unresolvable for now; the resolver reports it. + } + if isDomain(target) { + return nil + } + return fmt.Errorf("%s.domainIdRef: referenced %s %q is a project, not a domain: reference a %s created with isDomain: true", site, projectKind, name, projectKind) +} + +func (v *validator) checkSelector(ctx context.Context, mg resource.Managed, paved *fieldpath.Paved, gv schema.GroupVersion, site string) error { + labels, err := paved.GetStringObject(site + ".domainIdSelector.matchLabels") + if err != nil || len(labels) == 0 { + return nil //nolint:nilerr // No reference configured for this site. + } + + name, target, err := v.selectProject(ctx, mg, paved, gv, site, labels) + if err != nil || target == nil { + return nil //nolint:nilerr // Unresolvable for now; the resolver reports it. + } + if isDomain(target) { + return nil + } + + return fmt.Errorf("%s.domainIdSelector: selected %s %q is a project, not a domain: adjust the selector to match a %s created with isDomain: true", site, projectKind, name, projectKind) +} + +// selectProject mirrors how reference.APIResolver picks a target for a +// selector, so the reported name is the one the resolver would use. +func (v *validator) selectProject(ctx context.Context, mg resource.Managed, paved *fieldpath.Paved, gv schema.GroupVersion, site string, labels map[string]string) (string, *fieldpath.Paved, error) { + lo, err := v.client.Scheme().New(gv.WithKind(projectKind + "List")) + if err != nil { + return "", nil, err + } + list, ok := lo.(client.ObjectList) + if !ok { + return "", nil, fmt.Errorf("%T is not a client.ObjectList", lo) + } + if err := v.client.List(ctx, list, client.MatchingLabels(labels), client.InNamespace(refNamespace(paved, site+".domainIdSelector.namespace", mg))); err != nil { + return "", nil, err + } + + items, err := apimeta.ExtractList(list) + if err != nil { + return "", nil, err + } + + mustMatchController, _ := paved.GetBool(site + ".domainIdSelector.matchControllerRef") + for _, item := range items { + obj, ok := item.(client.Object) + if !ok { + continue + } + if mustMatchController && !meta.HaveSameController(mg, obj) { + continue + } + target, err := fieldpath.PaveObject(obj) + if err != nil { + return "", nil, err + } + return obj.GetName(), target, nil + } + + return "", nil, nil +} + +func (v *validator) getProject(ctx context.Context, gv schema.GroupVersion, nn types.NamespacedName) (*fieldpath.Paved, error) { + o, err := v.client.Scheme().New(gv.WithKind(projectKind)) + if err != nil { + return nil, err + } + obj, ok := o.(client.Object) + if !ok { + return nil, fmt.Errorf("%T is not a client.Object", o) + } + if err := v.client.Get(ctx, nn, obj); err != nil { + return nil, err + } + + return fieldpath.PaveObject(obj) +} + +func isDomain(p *fieldpath.Paved) bool { + for _, path := range isDomainPaths { + if v, err := p.GetBool(path); err == nil && v { + return true + } + } + + return false +} + +// refNamespace reads an explicit namespace from the paved object at the given +// path, falling back to the managed resource's own namespace. This mirrors +// how APINamespacedResolver resolves NamespacedReference.Namespace in +// crossplane-runtime. +func refNamespace(paved *fieldpath.Paved, path string, mg resource.Managed) string { + if ns, err := paved.GetString(path); err == nil && ns != "" { + return ns + } + return mg.GetNamespace() +} diff --git a/internal/initializer/domain/domain_test.go b/internal/initializer/domain/domain_test.go new file mode 100644 index 00000000..8f0e973e --- /dev/null +++ b/internal/initializer/domain/domain_test.go @@ -0,0 +1,445 @@ +/* +Copyright 2026 The Crossplane Authors. +*/ + +package domain + +import ( + "context" + "strings" + "testing" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + xpv1 "github.com/crossplane/crossplane-runtime/v2/apis/common/v1" + + identityv1alpha1 "github.com/crossplane-contrib/provider-openstack/apis/cluster/identity/v1alpha1" + nsidentityv1alpha1 "github.com/crossplane-contrib/provider-openstack/apis/namespaced/identity/v1alpha1" +) + +func ptr[T any](v T) *T { return &v } + +func newScheme(t *testing.T) *runtime.Scheme { + t.Helper() + s := runtime.NewScheme() + if err := identityv1alpha1.SchemeBuilder.AddToScheme(s); err != nil { + t.Fatal(err) + } + return s +} + +func newDomain(name string) *identityv1alpha1.ProjectV3 { + return &identityv1alpha1.ProjectV3{ + ObjectMeta: metav1.ObjectMeta{Name: name}, + Spec: identityv1alpha1.ProjectV3Spec{ + ForProvider: identityv1alpha1.ProjectV3Parameters{ + IsDomain: ptr(true), + Name: ptr(name), + }, + }, + } +} + +func newProject(name string) *identityv1alpha1.ProjectV3 { + return &identityv1alpha1.ProjectV3{ + ObjectMeta: metav1.ObjectMeta{Name: name}, + Spec: identityv1alpha1.ProjectV3Spec{ + ForProvider: identityv1alpha1.ProjectV3Parameters{ + Name: ptr(name), + }, + }, + } +} + +func newProjectWithLabels(name string, labels map[string]string) *identityv1alpha1.ProjectV3 { + p := newProject(name) + p.Labels = labels + return p +} + +func newDomainWithLabels(name string, labels map[string]string) *identityv1alpha1.ProjectV3 { + d := newDomain(name) + d.Labels = labels + return d +} + +func TestInitialize(t *testing.T) { + tests := []struct { + name string + existing []runtime.Object + managed *identityv1alpha1.ProjectV3 + wantErr bool + errSubstr string + }{ + { + name: "NoRef_NoError", + managed: &identityv1alpha1.ProjectV3{ + ObjectMeta: metav1.ObjectMeta{Name: "my-project"}, + Spec: identityv1alpha1.ProjectV3Spec{ + ForProvider: identityv1alpha1.ProjectV3Parameters{ + Name: ptr("my-project"), + }, + }, + }, + }, + { + name: "RefToDomain_NoError", + existing: []runtime.Object{newDomain("my-domain")}, + managed: &identityv1alpha1.ProjectV3{ + ObjectMeta: metav1.ObjectMeta{Name: "my-project"}, + Spec: identityv1alpha1.ProjectV3Spec{ + ForProvider: identityv1alpha1.ProjectV3Parameters{ + Name: ptr("my-project"), + DomainIDRef: &xpv1.Reference{Name: "my-domain"}, + }, + }, + }, + }, + { + name: "RefToProject_Error", + existing: []runtime.Object{newProject("not-a-domain")}, + managed: &identityv1alpha1.ProjectV3{ + ObjectMeta: metav1.ObjectMeta{Name: "child-project"}, + Spec: identityv1alpha1.ProjectV3Spec{ + ForProvider: identityv1alpha1.ProjectV3Parameters{ + Name: ptr("child-project"), + DomainIDRef: &xpv1.Reference{Name: "not-a-domain"}, + }, + }, + }, + wantErr: true, + errSubstr: "is a project, not a domain", + }, + { + name: "RefToNonExistent_NoError", + managed: &identityv1alpha1.ProjectV3{ + ObjectMeta: metav1.ObjectMeta{Name: "my-project"}, + Spec: identityv1alpha1.ProjectV3Spec{ + ForProvider: identityv1alpha1.ProjectV3Parameters{ + Name: ptr("my-project"), + DomainIDRef: &xpv1.Reference{Name: "does-not-exist"}, + }, + }, + }, + }, + { + name: "AlreadyResolved_SkipsCheck", + existing: []runtime.Object{newProject("not-a-domain")}, + managed: &identityv1alpha1.ProjectV3{ + ObjectMeta: metav1.ObjectMeta{Name: "my-project"}, + Spec: identityv1alpha1.ProjectV3Spec{ + ForProvider: identityv1alpha1.ProjectV3Parameters{ + Name: ptr("my-project"), + DomainID: ptr("some-uuid-already-resolved"), + DomainIDRef: &xpv1.Reference{Name: "not-a-domain"}, + }, + }, + }, + }, + { + name: "SelectorToDomain_NoError", + existing: []runtime.Object{newDomainWithLabels("sel-domain", map[string]string{ + "app": "test", + })}, + managed: &identityv1alpha1.ProjectV3{ + ObjectMeta: metav1.ObjectMeta{Name: "my-project"}, + Spec: identityv1alpha1.ProjectV3Spec{ + ForProvider: identityv1alpha1.ProjectV3Parameters{ + Name: ptr("my-project"), + DomainIDSelector: &xpv1.Selector{ + MatchLabels: map[string]string{"app": "test"}, + }, + }, + }, + }, + }, + { + name: "SelectorToProject_Error", + existing: []runtime.Object{newProjectWithLabels("sel-project", map[string]string{ + "app": "test", + })}, + managed: &identityv1alpha1.ProjectV3{ + ObjectMeta: metav1.ObjectMeta{Name: "my-project"}, + Spec: identityv1alpha1.ProjectV3Spec{ + ForProvider: identityv1alpha1.ProjectV3Parameters{ + Name: ptr("my-project"), + DomainIDSelector: &xpv1.Selector{ + MatchLabels: map[string]string{"app": "test"}, + }, + }, + }, + }, + wantErr: true, + errSubstr: "is a project, not a domain", + }, + { + name: "SelectorNoMatch_NoError", + managed: &identityv1alpha1.ProjectV3{ + ObjectMeta: metav1.ObjectMeta{Name: "my-project"}, + Spec: identityv1alpha1.ProjectV3Spec{ + ForProvider: identityv1alpha1.ProjectV3Parameters{ + Name: ptr("my-project"), + DomainIDSelector: &xpv1.Selector{ + MatchLabels: map[string]string{"missing": "label"}, + }, + }, + }, + }, + }, + { + name: "InitProviderRefToProject_Error", + existing: []runtime.Object{newProject("init-project")}, + managed: &identityv1alpha1.ProjectV3{ + ObjectMeta: metav1.ObjectMeta{Name: "my-project"}, + Spec: identityv1alpha1.ProjectV3Spec{ + ForProvider: identityv1alpha1.ProjectV3Parameters{ + Name: ptr("my-project"), + }, + InitProvider: identityv1alpha1.ProjectV3InitParameters{ + DomainIDRef: &xpv1.Reference{Name: "init-project"}, + }, + }, + }, + wantErr: true, + errSubstr: "spec.initProvider.domainIdRef", + }, + { + name: "IsDomainInStatus_NoError", + existing: []runtime.Object{statusDomain("status-domain")}, + managed: &identityv1alpha1.ProjectV3{ + ObjectMeta: metav1.ObjectMeta{Name: "my-project"}, + Spec: identityv1alpha1.ProjectV3Spec{ + ForProvider: identityv1alpha1.ProjectV3Parameters{ + Name: ptr("my-project"), + DomainIDRef: &xpv1.Reference{Name: "status-domain"}, + }, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + scheme := newScheme(t) + builder := fake.NewClientBuilder().WithScheme(scheme) + if len(tt.existing) > 0 { + builder = builder.WithRuntimeObjects(tt.existing...) + } + c := builder.Build() + + v := NewValidator(c) + err := v.Initialize(context.Background(), tt.managed) + + if tt.wantErr { + if err == nil { + t.Fatal("expected error, got nil") + } + if !strings.Contains(err.Error(), tt.errSubstr) { + t.Errorf("error %q does not contain %q", err.Error(), tt.errSubstr) + } + return + } + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + }) + } +} + +func statusDomain(name string) *identityv1alpha1.ProjectV3 { + return &identityv1alpha1.ProjectV3{ + ObjectMeta: metav1.ObjectMeta{Name: name}, + Spec: identityv1alpha1.ProjectV3Spec{ + ForProvider: identityv1alpha1.ProjectV3Parameters{ + Name: ptr(name), + }, + }, + Status: identityv1alpha1.ProjectV3Status{ + AtProvider: identityv1alpha1.ProjectV3Observation{ + IsDomain: ptr(true), + }, + }, + } +} + +func newNSScheme(t *testing.T) *runtime.Scheme { + t.Helper() + s := runtime.NewScheme() + if err := nsidentityv1alpha1.SchemeBuilder.AddToScheme(s); err != nil { + t.Fatal(err) + } + return s +} + +func newNSDomain(name, ns string) *nsidentityv1alpha1.ProjectV3 { + return &nsidentityv1alpha1.ProjectV3{ + ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: ns}, + Spec: nsidentityv1alpha1.ProjectV3Spec{ + ForProvider: nsidentityv1alpha1.ProjectV3Parameters{ + IsDomain: ptr(true), + Name: ptr(name), + }, + }, + } +} + +func newNSProject(name, ns string) *nsidentityv1alpha1.ProjectV3 { + return &nsidentityv1alpha1.ProjectV3{ + ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: ns}, + Spec: nsidentityv1alpha1.ProjectV3Spec{ + ForProvider: nsidentityv1alpha1.ProjectV3Parameters{ + Name: ptr(name), + }, + }, + } +} + +func TestInitializeCrossNamespace(t *testing.T) { + tests := []struct { + name string + existing []runtime.Object + managed *nsidentityv1alpha1.ProjectV3 + wantErr bool + errSubstr string + }{ + { + name: "RefCrossNS_DomainInTargetNS_NoError", + existing: []runtime.Object{ + newNSDomain("shared-domain", "infrastructure"), + newNSProject("shared-domain", "application"), + }, + managed: &nsidentityv1alpha1.ProjectV3{ + ObjectMeta: metav1.ObjectMeta{Name: "my-project", Namespace: "application"}, + Spec: nsidentityv1alpha1.ProjectV3Spec{ + ForProvider: nsidentityv1alpha1.ProjectV3Parameters{ + Name: ptr("my-project"), + DomainIDRef: &xpv1.NamespacedReference{ + Name: "shared-domain", + Namespace: "infrastructure", + }, + }, + }, + }, + }, + { + name: "RefCrossNS_ProjectInTargetNS_Error", + existing: []runtime.Object{ + newNSProject("not-a-domain", "infrastructure"), + }, + managed: &nsidentityv1alpha1.ProjectV3{ + ObjectMeta: metav1.ObjectMeta{Name: "my-project", Namespace: "application"}, + Spec: nsidentityv1alpha1.ProjectV3Spec{ + ForProvider: nsidentityv1alpha1.ProjectV3Parameters{ + Name: ptr("my-project"), + DomainIDRef: &xpv1.NamespacedReference{ + Name: "not-a-domain", + Namespace: "infrastructure", + }, + }, + }, + }, + wantErr: true, + errSubstr: "is a project, not a domain", + }, + { + name: "RefNoExplicitNS_FallsBackToMgNS", + existing: []runtime.Object{ + newNSDomain("local-domain", "application"), + }, + managed: &nsidentityv1alpha1.ProjectV3{ + ObjectMeta: metav1.ObjectMeta{Name: "my-project", Namespace: "application"}, + Spec: nsidentityv1alpha1.ProjectV3Spec{ + ForProvider: nsidentityv1alpha1.ProjectV3Parameters{ + Name: ptr("my-project"), + DomainIDRef: &xpv1.NamespacedReference{ + Name: "local-domain", + }, + }, + }, + }, + }, + { + name: "SelectorCrossNS_DomainInTargetNS_NoError", + existing: []runtime.Object{ + newNSProjectWithLabels("shared-domain", "infrastructure", map[string]string{"type": "domain"}, true), + newNSProjectWithLabels("same-name", "application", map[string]string{"type": "domain"}, false), + }, + managed: &nsidentityv1alpha1.ProjectV3{ + ObjectMeta: metav1.ObjectMeta{Name: "my-project", Namespace: "application"}, + Spec: nsidentityv1alpha1.ProjectV3Spec{ + ForProvider: nsidentityv1alpha1.ProjectV3Parameters{ + Name: ptr("my-project"), + DomainIDSelector: &xpv1.NamespacedSelector{ + MatchLabels: map[string]string{"type": "domain"}, + Namespace: "infrastructure", + }, + }, + }, + }, + }, + { + name: "SelectorCrossNS_ProjectInTargetNS_Error", + existing: []runtime.Object{ + newNSProjectWithLabels("infra-project", "infrastructure", map[string]string{"type": "domain"}, false), + }, + managed: &nsidentityv1alpha1.ProjectV3{ + ObjectMeta: metav1.ObjectMeta{Name: "my-project", Namespace: "application"}, + Spec: nsidentityv1alpha1.ProjectV3Spec{ + ForProvider: nsidentityv1alpha1.ProjectV3Parameters{ + Name: ptr("my-project"), + DomainIDSelector: &xpv1.NamespacedSelector{ + MatchLabels: map[string]string{"type": "domain"}, + Namespace: "infrastructure", + }, + }, + }, + }, + wantErr: true, + errSubstr: "is a project, not a domain", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + scheme := newNSScheme(t) + builder := fake.NewClientBuilder().WithScheme(scheme) + if len(tt.existing) > 0 { + builder = builder.WithRuntimeObjects(tt.existing...) + } + c := builder.Build() + + v := NewValidator(c) + err := v.Initialize(context.Background(), tt.managed) + + if tt.wantErr { + if err == nil { + t.Fatal("expected error, got nil") + } + if !strings.Contains(err.Error(), tt.errSubstr) { + t.Errorf("error %q does not contain %q", err.Error(), tt.errSubstr) + } + return + } + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + }) + } +} + +func newNSProjectWithLabels(name, ns string, labels map[string]string, isDomain bool) *nsidentityv1alpha1.ProjectV3 { + p := &nsidentityv1alpha1.ProjectV3{ + ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: ns, Labels: labels}, + Spec: nsidentityv1alpha1.ProjectV3Spec{ + ForProvider: nsidentityv1alpha1.ProjectV3Parameters{ + Name: ptr(name), + }, + }, + } + if isDomain { + p.Spec.ForProvider.IsDomain = ptr(true) + } + return p +} diff --git a/package/crds/identity.openstack.crossplane.io_groupv3s.yaml b/package/crds/identity.openstack.crossplane.io_groupv3s.yaml index 84979c08..540fdcf9 100644 --- a/package/crds/identity.openstack.crossplane.io_groupv3s.yaml +++ b/package/crds/identity.openstack.crossplane.io_groupv3s.yaml @@ -79,6 +79,82 @@ spec: domainId: description: The domain the group belongs to. type: string + domainIdRef: + description: Reference to a ProjectV3 in identity to populate + domainId. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + domainIdSelector: + description: Selector for a ProjectV3 in identity to populate + domainId. + properties: + matchControllerRef: + description: |- + MatchControllerRef ensures an object with the same controller reference + as the selecting object is selected. + type: boolean + matchLabels: + additionalProperties: + type: string + description: MatchLabels ensures an object with matching labels + is selected. + type: object + policy: + description: Policies for selection. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + type: object name: description: The name of the group. type: string @@ -108,6 +184,82 @@ spec: domainId: description: The domain the group belongs to. type: string + domainIdRef: + description: Reference to a ProjectV3 in identity to populate + domainId. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + domainIdSelector: + description: Selector for a ProjectV3 in identity to populate + domainId. + properties: + matchControllerRef: + description: |- + MatchControllerRef ensures an object with the same controller reference + as the selecting object is selected. + type: boolean + matchLabels: + additionalProperties: + type: string + description: MatchLabels ensures an object with matching labels + is selected. + type: object + policy: + description: Policies for selection. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + type: object name: description: The name of the group. type: string diff --git a/package/crds/identity.openstack.crossplane.io_inheritroleassignmentv3s.yaml b/package/crds/identity.openstack.crossplane.io_inheritroleassignmentv3s.yaml index 26909a29..c66f5a6b 100644 --- a/package/crds/identity.openstack.crossplane.io_inheritroleassignmentv3s.yaml +++ b/package/crds/identity.openstack.crossplane.io_inheritroleassignmentv3s.yaml @@ -77,6 +77,82 @@ spec: domainId: description: The domain to assign the role in. type: string + domainIdRef: + description: Reference to a ProjectV3 in identity to populate + domainId. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + domainIdSelector: + description: Selector for a ProjectV3 in identity to populate + domainId. + properties: + matchControllerRef: + description: |- + MatchControllerRef ensures an object with the same controller reference + as the selecting object is selected. + type: boolean + matchLabels: + additionalProperties: + type: string + description: MatchLabels ensures an object with matching labels + is selected. + type: object + policy: + description: Policies for selection. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + type: object groupId: description: The group to assign the role to. type: string @@ -262,6 +338,82 @@ spec: domainId: description: The domain to assign the role in. type: string + domainIdRef: + description: Reference to a ProjectV3 in identity to populate + domainId. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + domainIdSelector: + description: Selector for a ProjectV3 in identity to populate + domainId. + properties: + matchControllerRef: + description: |- + MatchControllerRef ensures an object with the same controller reference + as the selecting object is selected. + type: boolean + matchLabels: + additionalProperties: + type: string + description: MatchLabels ensures an object with matching labels + is selected. + type: object + policy: + description: Policies for selection. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + type: object groupId: description: The group to assign the role to. type: string diff --git a/package/crds/identity.openstack.crossplane.io_projectv3s.yaml b/package/crds/identity.openstack.crossplane.io_projectv3s.yaml index e0955832..a526fc50 100644 --- a/package/crds/identity.openstack.crossplane.io_projectv3s.yaml +++ b/package/crds/identity.openstack.crossplane.io_projectv3s.yaml @@ -79,6 +79,82 @@ spec: domainId: description: The domain this project belongs to. type: string + domainIdRef: + description: Reference to a ProjectV3 in identity to populate + domainId. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + domainIdSelector: + description: Selector for a ProjectV3 in identity to populate + domainId. + properties: + matchControllerRef: + description: |- + MatchControllerRef ensures an object with the same controller reference + as the selecting object is selected. + type: boolean + matchLabels: + additionalProperties: + type: string + description: MatchLabels ensures an object with matching labels + is selected. + type: object + policy: + description: Policies for selection. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + type: object enabled: description: |- Whether the project is enabled or disabled. Valid @@ -132,6 +208,82 @@ spec: domainId: description: The domain this project belongs to. type: string + domainIdRef: + description: Reference to a ProjectV3 in identity to populate + domainId. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + domainIdSelector: + description: Selector for a ProjectV3 in identity to populate + domainId. + properties: + matchControllerRef: + description: |- + MatchControllerRef ensures an object with the same controller reference + as the selecting object is selected. + type: boolean + matchLabels: + additionalProperties: + type: string + description: MatchLabels ensures an object with matching labels + is selected. + type: object + policy: + description: Policies for selection. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + type: object enabled: description: |- Whether the project is enabled or disabled. Valid diff --git a/package/crds/identity.openstack.crossplane.io_roleassignmentv3s.yaml b/package/crds/identity.openstack.crossplane.io_roleassignmentv3s.yaml index 464aae0d..776e767f 100644 --- a/package/crds/identity.openstack.crossplane.io_roleassignmentv3s.yaml +++ b/package/crds/identity.openstack.crossplane.io_roleassignmentv3s.yaml @@ -76,6 +76,82 @@ spec: domainId: description: The domain to assign the role in. type: string + domainIdRef: + description: Reference to a ProjectV3 in identity to populate + domainId. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + domainIdSelector: + description: Selector for a ProjectV3 in identity to populate + domainId. + properties: + matchControllerRef: + description: |- + MatchControllerRef ensures an object with the same controller reference + as the selecting object is selected. + type: boolean + matchLabels: + additionalProperties: + type: string + description: MatchLabels ensures an object with matching labels + is selected. + type: object + policy: + description: Policies for selection. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + type: object groupId: description: The group to assign the role to. type: string @@ -335,6 +411,82 @@ spec: domainId: description: The domain to assign the role in. type: string + domainIdRef: + description: Reference to a ProjectV3 in identity to populate + domainId. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + domainIdSelector: + description: Selector for a ProjectV3 in identity to populate + domainId. + properties: + matchControllerRef: + description: |- + MatchControllerRef ensures an object with the same controller reference + as the selecting object is selected. + type: boolean + matchLabels: + additionalProperties: + type: string + description: MatchLabels ensures an object with matching labels + is selected. + type: object + policy: + description: Policies for selection. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + type: object groupId: description: The group to assign the role to. type: string diff --git a/package/crds/identity.openstack.crossplane.io_rolev3s.yaml b/package/crds/identity.openstack.crossplane.io_rolev3s.yaml index e19ff823..69f28061 100644 --- a/package/crds/identity.openstack.crossplane.io_rolev3s.yaml +++ b/package/crds/identity.openstack.crossplane.io_rolev3s.yaml @@ -76,6 +76,82 @@ spec: domainId: description: The domain the role belongs to. type: string + domainIdRef: + description: Reference to a ProjectV3 in identity to populate + domainId. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + domainIdSelector: + description: Selector for a ProjectV3 in identity to populate + domainId. + properties: + matchControllerRef: + description: |- + MatchControllerRef ensures an object with the same controller reference + as the selecting object is selected. + type: boolean + matchLabels: + additionalProperties: + type: string + description: MatchLabels ensures an object with matching labels + is selected. + type: object + policy: + description: Policies for selection. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + type: object name: description: The name of the role. type: string @@ -102,6 +178,82 @@ spec: domainId: description: The domain the role belongs to. type: string + domainIdRef: + description: Reference to a ProjectV3 in identity to populate + domainId. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + domainIdSelector: + description: Selector for a ProjectV3 in identity to populate + domainId. + properties: + matchControllerRef: + description: |- + MatchControllerRef ensures an object with the same controller reference + as the selecting object is selected. + type: boolean + matchLabels: + additionalProperties: + type: string + description: MatchLabels ensures an object with matching labels + is selected. + type: object + policy: + description: Policies for selection. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + type: object name: description: The name of the role. type: string diff --git a/package/crds/identity.openstack.crossplane.io_userv3s.yaml b/package/crds/identity.openstack.crossplane.io_userv3s.yaml index fc510d84..08ad8969 100644 --- a/package/crds/identity.openstack.crossplane.io_userv3s.yaml +++ b/package/crds/identity.openstack.crossplane.io_userv3s.yaml @@ -158,6 +158,82 @@ spec: domainId: description: The domain this user belongs to. type: string + domainIdRef: + description: Reference to a ProjectV3 in identity to populate + domainId. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + domainIdSelector: + description: Selector for a ProjectV3 in identity to populate + domainId. + properties: + matchControllerRef: + description: |- + MatchControllerRef ensures an object with the same controller reference + as the selecting object is selected. + type: boolean + matchLabels: + additionalProperties: + type: string + description: MatchLabels ensures an object with matching labels + is selected. + type: object + policy: + description: Policies for selection. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + type: object enabled: description: |- Whether the user is enabled or disabled. Valid @@ -331,6 +407,82 @@ spec: domainId: description: The domain this user belongs to. type: string + domainIdRef: + description: Reference to a ProjectV3 in identity to populate + domainId. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + domainIdSelector: + description: Selector for a ProjectV3 in identity to populate + domainId. + properties: + matchControllerRef: + description: |- + MatchControllerRef ensures an object with the same controller reference + as the selecting object is selected. + type: boolean + matchLabels: + additionalProperties: + type: string + description: MatchLabels ensures an object with matching labels + is selected. + type: object + policy: + description: Policies for selection. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + type: object enabled: description: |- Whether the user is enabled or disabled. Valid diff --git a/package/crds/identity.openstack.m.crossplane.io_groupv3s.yaml b/package/crds/identity.openstack.m.crossplane.io_groupv3s.yaml index e43cc959..01055481 100644 --- a/package/crds/identity.openstack.m.crossplane.io_groupv3s.yaml +++ b/package/crds/identity.openstack.m.crossplane.io_groupv3s.yaml @@ -65,6 +65,88 @@ spec: domainId: description: The domain the group belongs to. type: string + domainIdRef: + description: Reference to a ProjectV3 in identity to populate + domainId. + properties: + name: + description: Name of the referenced object. + type: string + namespace: + description: Namespace of the referenced object + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + domainIdSelector: + description: Selector for a ProjectV3 in identity to populate + domainId. + properties: + matchControllerRef: + description: |- + MatchControllerRef ensures an object with the same controller reference + as the selecting object is selected. + type: boolean + matchLabels: + additionalProperties: + type: string + description: MatchLabels ensures an object with matching labels + is selected. + type: object + namespace: + description: Namespace for the selector + type: string + policy: + description: Policies for selection. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + type: object name: description: The name of the group. type: string @@ -94,6 +176,88 @@ spec: domainId: description: The domain the group belongs to. type: string + domainIdRef: + description: Reference to a ProjectV3 in identity to populate + domainId. + properties: + name: + description: Name of the referenced object. + type: string + namespace: + description: Namespace of the referenced object + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + domainIdSelector: + description: Selector for a ProjectV3 in identity to populate + domainId. + properties: + matchControllerRef: + description: |- + MatchControllerRef ensures an object with the same controller reference + as the selecting object is selected. + type: boolean + matchLabels: + additionalProperties: + type: string + description: MatchLabels ensures an object with matching labels + is selected. + type: object + namespace: + description: Namespace for the selector + type: string + policy: + description: Policies for selection. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + type: object name: description: The name of the group. type: string diff --git a/package/crds/identity.openstack.m.crossplane.io_inheritroleassignmentv3s.yaml b/package/crds/identity.openstack.m.crossplane.io_inheritroleassignmentv3s.yaml index 4cefdc85..a4d924b5 100644 --- a/package/crds/identity.openstack.m.crossplane.io_inheritroleassignmentv3s.yaml +++ b/package/crds/identity.openstack.m.crossplane.io_inheritroleassignmentv3s.yaml @@ -63,6 +63,88 @@ spec: domainId: description: The domain to assign the role in. type: string + domainIdRef: + description: Reference to a ProjectV3 in identity to populate + domainId. + properties: + name: + description: Name of the referenced object. + type: string + namespace: + description: Namespace of the referenced object + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + domainIdSelector: + description: Selector for a ProjectV3 in identity to populate + domainId. + properties: + matchControllerRef: + description: |- + MatchControllerRef ensures an object with the same controller reference + as the selecting object is selected. + type: boolean + matchLabels: + additionalProperties: + type: string + description: MatchLabels ensures an object with matching labels + is selected. + type: object + namespace: + description: Namespace for the selector + type: string + policy: + description: Policies for selection. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + type: object groupId: description: The group to assign the role to. type: string @@ -260,6 +342,88 @@ spec: domainId: description: The domain to assign the role in. type: string + domainIdRef: + description: Reference to a ProjectV3 in identity to populate + domainId. + properties: + name: + description: Name of the referenced object. + type: string + namespace: + description: Namespace of the referenced object + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + domainIdSelector: + description: Selector for a ProjectV3 in identity to populate + domainId. + properties: + matchControllerRef: + description: |- + MatchControllerRef ensures an object with the same controller reference + as the selecting object is selected. + type: boolean + matchLabels: + additionalProperties: + type: string + description: MatchLabels ensures an object with matching labels + is selected. + type: object + namespace: + description: Namespace for the selector + type: string + policy: + description: Policies for selection. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + type: object groupId: description: The group to assign the role to. type: string diff --git a/package/crds/identity.openstack.m.crossplane.io_projectv3s.yaml b/package/crds/identity.openstack.m.crossplane.io_projectv3s.yaml index bc6a2d83..4ef888d3 100644 --- a/package/crds/identity.openstack.m.crossplane.io_projectv3s.yaml +++ b/package/crds/identity.openstack.m.crossplane.io_projectv3s.yaml @@ -65,6 +65,88 @@ spec: domainId: description: The domain this project belongs to. type: string + domainIdRef: + description: Reference to a ProjectV3 in identity to populate + domainId. + properties: + name: + description: Name of the referenced object. + type: string + namespace: + description: Namespace of the referenced object + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + domainIdSelector: + description: Selector for a ProjectV3 in identity to populate + domainId. + properties: + matchControllerRef: + description: |- + MatchControllerRef ensures an object with the same controller reference + as the selecting object is selected. + type: boolean + matchLabels: + additionalProperties: + type: string + description: MatchLabels ensures an object with matching labels + is selected. + type: object + namespace: + description: Namespace for the selector + type: string + policy: + description: Policies for selection. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + type: object enabled: description: |- Whether the project is enabled or disabled. Valid @@ -118,6 +200,88 @@ spec: domainId: description: The domain this project belongs to. type: string + domainIdRef: + description: Reference to a ProjectV3 in identity to populate + domainId. + properties: + name: + description: Name of the referenced object. + type: string + namespace: + description: Namespace of the referenced object + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + domainIdSelector: + description: Selector for a ProjectV3 in identity to populate + domainId. + properties: + matchControllerRef: + description: |- + MatchControllerRef ensures an object with the same controller reference + as the selecting object is selected. + type: boolean + matchLabels: + additionalProperties: + type: string + description: MatchLabels ensures an object with matching labels + is selected. + type: object + namespace: + description: Namespace for the selector + type: string + policy: + description: Policies for selection. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + type: object enabled: description: |- Whether the project is enabled or disabled. Valid diff --git a/package/crds/identity.openstack.m.crossplane.io_roleassignmentv3s.yaml b/package/crds/identity.openstack.m.crossplane.io_roleassignmentv3s.yaml index c1c8fa34..b452f127 100644 --- a/package/crds/identity.openstack.m.crossplane.io_roleassignmentv3s.yaml +++ b/package/crds/identity.openstack.m.crossplane.io_roleassignmentv3s.yaml @@ -62,6 +62,88 @@ spec: domainId: description: The domain to assign the role in. type: string + domainIdRef: + description: Reference to a ProjectV3 in identity to populate + domainId. + properties: + name: + description: Name of the referenced object. + type: string + namespace: + description: Namespace of the referenced object + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + domainIdSelector: + description: Selector for a ProjectV3 in identity to populate + domainId. + properties: + matchControllerRef: + description: |- + MatchControllerRef ensures an object with the same controller reference + as the selecting object is selected. + type: boolean + matchLabels: + additionalProperties: + type: string + description: MatchLabels ensures an object with matching labels + is selected. + type: object + namespace: + description: Namespace for the selector + type: string + policy: + description: Policies for selection. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + type: object groupId: description: The group to assign the role to. type: string @@ -339,6 +421,88 @@ spec: domainId: description: The domain to assign the role in. type: string + domainIdRef: + description: Reference to a ProjectV3 in identity to populate + domainId. + properties: + name: + description: Name of the referenced object. + type: string + namespace: + description: Namespace of the referenced object + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + domainIdSelector: + description: Selector for a ProjectV3 in identity to populate + domainId. + properties: + matchControllerRef: + description: |- + MatchControllerRef ensures an object with the same controller reference + as the selecting object is selected. + type: boolean + matchLabels: + additionalProperties: + type: string + description: MatchLabels ensures an object with matching labels + is selected. + type: object + namespace: + description: Namespace for the selector + type: string + policy: + description: Policies for selection. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + type: object groupId: description: The group to assign the role to. type: string diff --git a/package/crds/identity.openstack.m.crossplane.io_rolev3s.yaml b/package/crds/identity.openstack.m.crossplane.io_rolev3s.yaml index 11c2fb92..966f19cd 100644 --- a/package/crds/identity.openstack.m.crossplane.io_rolev3s.yaml +++ b/package/crds/identity.openstack.m.crossplane.io_rolev3s.yaml @@ -62,6 +62,88 @@ spec: domainId: description: The domain the role belongs to. type: string + domainIdRef: + description: Reference to a ProjectV3 in identity to populate + domainId. + properties: + name: + description: Name of the referenced object. + type: string + namespace: + description: Namespace of the referenced object + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + domainIdSelector: + description: Selector for a ProjectV3 in identity to populate + domainId. + properties: + matchControllerRef: + description: |- + MatchControllerRef ensures an object with the same controller reference + as the selecting object is selected. + type: boolean + matchLabels: + additionalProperties: + type: string + description: MatchLabels ensures an object with matching labels + is selected. + type: object + namespace: + description: Namespace for the selector + type: string + policy: + description: Policies for selection. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + type: object name: description: The name of the role. type: string @@ -88,6 +170,88 @@ spec: domainId: description: The domain the role belongs to. type: string + domainIdRef: + description: Reference to a ProjectV3 in identity to populate + domainId. + properties: + name: + description: Name of the referenced object. + type: string + namespace: + description: Namespace of the referenced object + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + domainIdSelector: + description: Selector for a ProjectV3 in identity to populate + domainId. + properties: + matchControllerRef: + description: |- + MatchControllerRef ensures an object with the same controller reference + as the selecting object is selected. + type: boolean + matchLabels: + additionalProperties: + type: string + description: MatchLabels ensures an object with matching labels + is selected. + type: object + namespace: + description: Namespace for the selector + type: string + policy: + description: Policies for selection. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + type: object name: description: The name of the role. type: string diff --git a/package/crds/identity.openstack.m.crossplane.io_userv3s.yaml b/package/crds/identity.openstack.m.crossplane.io_userv3s.yaml index f72a8a33..05ea6644 100644 --- a/package/crds/identity.openstack.m.crossplane.io_userv3s.yaml +++ b/package/crds/identity.openstack.m.crossplane.io_userv3s.yaml @@ -150,6 +150,88 @@ spec: domainId: description: The domain this user belongs to. type: string + domainIdRef: + description: Reference to a ProjectV3 in identity to populate + domainId. + properties: + name: + description: Name of the referenced object. + type: string + namespace: + description: Namespace of the referenced object + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + domainIdSelector: + description: Selector for a ProjectV3 in identity to populate + domainId. + properties: + matchControllerRef: + description: |- + MatchControllerRef ensures an object with the same controller reference + as the selecting object is selected. + type: boolean + matchLabels: + additionalProperties: + type: string + description: MatchLabels ensures an object with matching labels + is selected. + type: object + namespace: + description: Namespace for the selector + type: string + policy: + description: Policies for selection. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + type: object enabled: description: |- Whether the user is enabled or disabled. Valid @@ -324,6 +406,88 @@ spec: domainId: description: The domain this user belongs to. type: string + domainIdRef: + description: Reference to a ProjectV3 in identity to populate + domainId. + properties: + name: + description: Name of the referenced object. + type: string + namespace: + description: Namespace of the referenced object + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + domainIdSelector: + description: Selector for a ProjectV3 in identity to populate + domainId. + properties: + matchControllerRef: + description: |- + MatchControllerRef ensures an object with the same controller reference + as the selecting object is selected. + type: boolean + matchLabels: + additionalProperties: + type: string + description: MatchLabels ensures an object with matching labels + is selected. + type: object + namespace: + description: Namespace for the selector + type: string + policy: + description: Policies for selection. + properties: + resolution: + default: Required + description: |- + Resolution specifies whether resolution of this reference is required. + The default is 'Required', which means the reconcile will fail if the + reference cannot be resolved. 'Optional' means this reference will be + a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: |- + Resolve specifies when this reference should be resolved. The default + is 'IfNotPresent', which will attempt to resolve the reference only when + the corresponding field is not present. Use 'Always' to resolve the + reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + type: object enabled: description: |- Whether the user is enabled or disabled. Valid