-
Notifications
You must be signed in to change notification settings - Fork 242
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
628 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// Copyright 2025 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
// +kcc:proto=mockgcp.cloud.cloudidentity.groups.v1beta1 | ||
package v1beta1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
// Copyright 2025 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package v1beta1 | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/GoogleCloudPlatform/k8s-config-connector/apis/common" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
) | ||
|
||
// GroupIdentity defines the resource reference to CloudIdentityGroup, which "External" field | ||
// holds the GCP identifier for the KRM object. | ||
type GroupIdentity struct { | ||
id string | ||
} | ||
|
||
func (i *GroupIdentity) String() string { | ||
return "/groups/" + i.id | ||
} | ||
|
||
func (i *GroupIdentity) ID() string { | ||
return i.id | ||
} | ||
|
||
// NewGroupIdentity New builds a GroupIdentity from the Config Connector Group object. | ||
func NewGroupIdentity(ctx context.Context, reader client.Reader, obj *CloudIdentityGroup) (*GroupIdentity, error) { | ||
// Get desired ID | ||
resourceID := common.ValueOf(obj.Spec.ResourceID) | ||
if resourceID == "" { | ||
resourceID = obj.GetName() | ||
} | ||
if resourceID == "" { | ||
return nil, fmt.Errorf("cannot resolve resource ID") | ||
} | ||
|
||
// Use approved External | ||
externalRef := common.ValueOf(obj.Status.ExternalRef) | ||
if externalRef != "" { | ||
// Validate desired with actual | ||
actualResourceID, err := ParseGroupExternal(externalRef) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if actualResourceID != resourceID { | ||
return nil, fmt.Errorf("cannot reset `metadata.name` or `spec.resourceID` to %s, since it has already assigned to %s", | ||
resourceID, actualResourceID) | ||
} | ||
} | ||
return &GroupIdentity{ | ||
id: resourceID, | ||
}, nil | ||
} | ||
|
||
func ParseGroupExternal(external string) (resourceID string, err error) { | ||
tokens := strings.Split(external, "/") | ||
if len(tokens) != 2 || tokens[0] != "groups" { | ||
return "", fmt.Errorf("format of CloudIdentityGroup external=%q was not known (use groups/{{groupID}})", external) | ||
} | ||
resourceID = tokens[1] | ||
return resourceID, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
// Copyright 2025 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package v1beta1 | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
refsv1beta1 "github.com/GoogleCloudPlatform/k8s-config-connector/apis/refs/v1beta1" | ||
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/k8s" | ||
apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | ||
"k8s.io/apimachinery/pkg/types" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
) | ||
|
||
var _ refsv1beta1.ExternalNormalizer = &GroupRef{} | ||
|
||
// GroupRef defines the resource reference to CloudIdentityGroup, which "External" field | ||
// holds the GCP identifier for the KRM object. | ||
type GroupRef struct { | ||
// A reference to an externally managed CloudIdentityGroup resource. | ||
// Should be in the format "groups/{{groupID}}". | ||
External string `json:"external,omitempty"` | ||
|
||
// The name of a CloudIdentityGroup resource. | ||
Name string `json:"name,omitempty"` | ||
|
||
// The namespace of a CloudIdentityGroup resource. | ||
Namespace string `json:"namespace,omitempty"` | ||
} | ||
|
||
// NormalizedExternal provision the "External" value for other resource that depends on CloudIdentityGroup. | ||
// If the "External" is given in the other resource's spec.CloudIdentityGroupRef, the given value will be used. | ||
// Otherwise, the "Name" and "Namespace" will be used to query the actual CloudIdentityGroup object from the cluster. | ||
func (r *GroupRef) NormalizedExternal(ctx context.Context, reader client.Reader, otherNamespace string) (string, error) { | ||
if r.External != "" && r.Name != "" { | ||
return "", fmt.Errorf("cannot specify both name and external on %s reference", CloudIdentityGroupGVK.Kind) | ||
} | ||
// From given External | ||
if r.External != "" { | ||
if _, err := ParseGroupExternal(r.External); err != nil { | ||
return "", err | ||
} | ||
return r.External, nil | ||
} | ||
|
||
// From the Config Connector object | ||
if r.Namespace == "" { | ||
r.Namespace = otherNamespace | ||
} | ||
key := types.NamespacedName{Name: r.Name, Namespace: r.Namespace} | ||
u := &unstructured.Unstructured{} | ||
u.SetGroupVersionKind(CloudIdentityGroupGVK) | ||
if err := reader.Get(ctx, key, u); err != nil { | ||
if apierrors.IsNotFound(err) { | ||
return "", k8s.NewReferenceNotFoundError(u.GroupVersionKind(), key) | ||
} | ||
return "", fmt.Errorf("reading referenced %s %s: %w", CloudIdentityGroupGVK, key, err) | ||
} | ||
// Get external from status.externalRef. This is the most trustworthy place. | ||
actualExternalRef, _, err := unstructured.NestedString(u.Object, "status", "externalRef") | ||
if err != nil { | ||
return "", fmt.Errorf("reading status.externalRef: %w", err) | ||
} | ||
if actualExternalRef == "" { | ||
return "", k8s.NewReferenceNotReadyError(u.GroupVersionKind(), key) | ||
} | ||
r.External = actualExternalRef | ||
return r.External, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
// Copyright 2025 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package v1beta1 | ||
|
||
import ( | ||
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/k8s/v1alpha1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
) | ||
|
||
var CloudIdentityGroupGVK = GroupVersion.WithKind("CloudIdentityGroup") | ||
|
||
type GroupGroupKey struct { | ||
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Id field is immutable" | ||
/* Immutable. The ID of the entity. | ||
For Google-managed entities, the id must be the email address of an existing | ||
group or user. | ||
For external-identity-mapped entities, the id must be a string conforming | ||
to the Identity Source's requirements. | ||
Must be unique within a namespace. */ | ||
// +required | ||
Id string `json:"id"` | ||
|
||
/* Immutable. The namespace in which the entity exists. | ||
If not specified, the EntityKey represents a Google-managed entity | ||
such as a Google user or a Google Group. | ||
If specified, the EntityKey represents an external-identity-mapped group. | ||
The namespace must correspond to an identity source created in Admin Console | ||
and must be in the form of 'identitysources/{identity_source_id}'. */ | ||
Namespace *string `json:"namespace,omitempty"` | ||
} | ||
|
||
type CloudIdentityGroupSpec struct { | ||
/* An extended description to help users determine the purpose of a Group. | ||
Must not be longer than 4,096 characters. */ | ||
Description *string `json:"description,omitempty"` | ||
|
||
/* The display name of the Group. */ | ||
DisplayName *string `json:"displayName,omitempty"` | ||
|
||
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="GroupKey field is immutable" | ||
/* Immutable. EntityKey of the Group. */ | ||
// +required | ||
GroupKey GroupGroupKey `json:"groupKey"` | ||
|
||
/* Immutable. The initial configuration options for creating a Group. | ||
See the | ||
[API reference](https://cloud.google.com/identity/docs/reference/rest/v1beta1/groups/create#initialgroupconfig) | ||
for possible values. Default value: "EMPTY" Possible values: ["INITIAL_GROUP_CONFIG_UNSPECIFIED", "WITH_INITIAL_OWNER", "EMPTY"]. */ | ||
InitialGroupConfig *string `json:"initialGroupConfig,omitempty"` | ||
|
||
/* One or more label entries that apply to the Group. Currently supported labels contain a key with an empty value. | ||
Google Groups are the default type of group and have a label with a key of cloudidentity.googleapis.com/groups.discussion_forum and an empty value. | ||
Existing Google Groups can have an additional label with a key of cloudidentity.googleapis.com/groups.security and an empty value added to them. This is an immutable change and the security label cannot be removed once added. | ||
Dynamic groups have a label with a key of cloudidentity.googleapis.com/groups.dynamic. | ||
Identity-mapped groups for Cloud Search have a label with a key of system/groups/external and an empty value. */ | ||
// +required | ||
Labels map[string]string `json:"labels"` | ||
|
||
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Parent field is immutable" | ||
/* Immutable. The resource name of the entity under which this Group resides in the | ||
Cloud Identity resource hierarchy. | ||
Must be of the form identitysources/{identity_source_id} for external-identity-mapped | ||
groups or customers/{customer_id} for Google Groups. */ | ||
// +required | ||
Parent string `json:"parent"` | ||
|
||
/* Immutable. The service-generated name of the resource. Used for acquisition only. Leave unset to create a new resource. */ | ||
ResourceID *string `json:"resourceID,omitempty"` | ||
} | ||
|
||
type CloudIdentityGroupStatus struct { | ||
/* Conditions represent the latest available observations of the | ||
CloudIdentityGroup's current state. */ | ||
Conditions []v1alpha1.Condition `json:"conditions,omitempty"` | ||
|
||
/* The time when the Group was created. */ | ||
CreateTime *string `json:"createTime,omitempty"` | ||
|
||
/* Resource name of the Group in the format: groups/{group_id}, where group_id | ||
is the unique ID assigned to the Group. */ | ||
Name *string `json:"name,omitempty"` | ||
|
||
// A unique specifier for the CloudIdentityGroup resource in GCP. | ||
ExternalRef *string `json:"externalRef,omitempty"` | ||
|
||
/* ObservedGeneration is the generation of the resource that was most recently observed by the Config Connector controller. If this is equal to metadata.generation, then that means that the current reported status reflects the most recent desired state of the resource. */ | ||
ObservedGeneration *int64 `json:"observedGeneration,omitempty"` | ||
|
||
/* The time when the Group was last updated. */ | ||
UpdateTime *string `json:"updateTime,omitempty"` | ||
} | ||
|
||
// +genclient | ||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object | ||
// +kubebuilder:resource:categories=gcp,shortName=gcpcloudidentitygroup;gcpcloudidentitygroups | ||
// +kubebuilder:subresource:status | ||
// +kubebuilder:metadata:labels="cnrm.cloud.google.com/managed-by-kcc=true";"cnrm.cloud.google.com/stability-level=stable";"cnrm.cloud.google.com/system=true";"cnrm.cloud.google.com/tf2crd=true" | ||
// +kubebuilder:printcolumn:name="Age",JSONPath=".metadata.creationTimestamp",type="date" | ||
// +kubebuilder:printcolumn:name="Ready",JSONPath=".status.conditions[?(@.type=='Ready')].status",type="string",description="When 'True', the most recent reconcile of the resource succeeded" | ||
// +kubebuilder:printcolumn:name="Status",JSONPath=".status.conditions[?(@.type=='Ready')].reason",type="string",description="The reason for the value in 'Ready'" | ||
// +kubebuilder:printcolumn:name="Status Age",JSONPath=".status.conditions[?(@.type=='Ready')].lastTransitionTime",type="date",description="The last transition time for the value in 'Status'" | ||
|
||
// CloudIdentityGroup is the Schema for the cloudidentity API | ||
// +k8s:openapi-gen=true | ||
type CloudIdentityGroup struct { | ||
metav1.TypeMeta `json:",inline"` | ||
metav1.ObjectMeta `json:"metadata,omitempty"` | ||
|
||
Spec CloudIdentityGroupSpec `json:"spec,omitempty"` | ||
Status CloudIdentityGroupStatus `json:"status,omitempty"` | ||
} | ||
|
||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object | ||
|
||
// CloudIdentityGroupList contains a list of CloudIdentityGroup | ||
type CloudIdentityGroupList struct { | ||
metav1.TypeMeta `json:",inline"` | ||
metav1.ListMeta `json:"metadata,omitempty"` | ||
Items []CloudIdentityGroup `json:"items"` | ||
} | ||
|
||
func init() { | ||
SchemeBuilder.Register(&CloudIdentityGroup{}, &CloudIdentityGroupList{}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// Copyright 2025 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
// +kubebuilder:object:generate=true | ||
// +groupName=cloudidentity.cnrm.cloud.google.com | ||
package v1beta1 | ||
|
||
import ( | ||
"k8s.io/apimachinery/pkg/runtime/schema" | ||
"sigs.k8s.io/controller-runtime/pkg/scheme" | ||
) | ||
|
||
var ( | ||
// GroupVersion is group version used to register these objects | ||
GroupVersion = schema.GroupVersion{Group: "cloudidentity.cnrm.cloud.google.com", Version: "v1beta1"} | ||
|
||
// SchemeBuilder is used to add go types to the GroupVersionKind scheme | ||
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} | ||
|
||
// AddToScheme adds the types in this group-version to the given scheme. | ||
AddToScheme = SchemeBuilder.AddToScheme | ||
) |
Oops, something went wrong.