-
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.
Merge pull request #2785 from jingyih/IAPSettings
feat: add types and mapping functions for IAPSettings
- Loading branch information
Showing
16 changed files
with
1,478 additions
and
3 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 2024 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=google.cloud.iap.v1 | ||
package v1alpha1 |
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 2024 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=iap.cnrm.cloud.google.com | ||
package v1alpha1 | ||
|
||
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: "iap.cnrm.cloud.google.com", Version: "v1alpha1"} | ||
|
||
// 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 | ||
) |
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,144 @@ | ||
// Copyright 2024 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 v1alpha1 | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/GoogleCloudPlatform/k8s-config-connector/apis/common" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
) | ||
|
||
// IAPSettingsIdentity defines the resource reference to IAPSettings. | ||
// The id could have the following format: | ||
// | ||
// organizations/{organization_id} | ||
// folders/{folder_id} | ||
// projects/{projects_id} | ||
// projects/{projects_id}/iap_web | ||
// projects/{projects_id}/iap_web/compute | ||
// projects/{projects_id}/iap_web/compute-{region} | ||
// projects/{projects_id}/iap_web/compute/service/{service_id} | ||
// projects/{projects_id}/iap_web/compute-{region}/service/{service_id} | ||
// projects/{projects_id}/iap_web/appengine-{app_id} | ||
// projects/{projects_id}/iap_web/appengine-{app_id}/service/{service_id} | ||
// projects/{projects_id}/iap_web/appengine-{app_id}/service/{service_id}/version/{version_id} | ||
type IAPSettingsIdentity struct { | ||
id string | ||
} | ||
|
||
func (i *IAPSettingsIdentity) String() string { | ||
return i.id | ||
} | ||
|
||
func (i *IAPSettingsIdentity) ID() string { | ||
return i.id | ||
} | ||
|
||
// New builds a IAPSettingsIdentity from the Config Connector IAPSettings object. | ||
func NewIAPSettingsIdentity(ctx context.Context, reader client.Reader, obj *IAPSettings) (*IAPSettingsIdentity, error) { | ||
// Get desired ID | ||
// Note that we cannot use `metadata.name` as resourceID since the supported resource ID formats are not valid Kubernetes names. | ||
resourceID := common.ValueOf(obj.Spec.ResourceID) | ||
if resourceID == "" { | ||
resourceID = common.ValueOf(obj.Spec.Name) | ||
} | ||
if resourceID == "" { | ||
return nil, fmt.Errorf("cannot resolve resource ID") | ||
} | ||
if err := ValidateIAPSettingsID(resourceID); err != nil { | ||
return nil, err | ||
} | ||
|
||
// Use approved External | ||
externalRef := common.ValueOf(obj.Status.ExternalRef) | ||
if externalRef != "" { | ||
// Validate desired with actual | ||
actualResourceID := externalRef | ||
if err := ValidateIAPSettingsID(actualResourceID); err != nil { | ||
return nil, err | ||
} | ||
if actualResourceID != resourceID { | ||
return nil, fmt.Errorf("cannot reset `spec.name` or `spec.resourceID` to %s, since it has already assigned to %s", | ||
resourceID, actualResourceID) | ||
} | ||
} | ||
return &IAPSettingsIdentity{ | ||
id: resourceID, | ||
}, nil | ||
} | ||
|
||
// ValidateIAPSettingsID validates the IAPSettings resource ID. | ||
func ValidateIAPSettingsID(id string) error { | ||
if id == "" { | ||
return fmt.Errorf("id cannot be empty") | ||
} | ||
|
||
parts := strings.Split(id, "/") | ||
if len(parts) < 2 { | ||
return fmt.Errorf("invalid IAP settings ID format %q: must have at least 2 segments (e.g., 'projects/my-project')", id) | ||
} | ||
|
||
// Validate root resource type | ||
switch parts[0] { | ||
case "organizations", "folders", "projects": | ||
// Valid root types | ||
default: | ||
return fmt.Errorf("invalid root resource type %q: must be one of: organizations, folders, projects", parts[0]) | ||
} | ||
|
||
// For organization and folder paths, only expect 2 parts | ||
if parts[0] == "organizations" || parts[0] == "folders" { | ||
if len(parts) != 2 { | ||
return fmt.Errorf("invalid %s IAP settings path %q: must have exactly 2 segments (e.g., '%s/my-id')", parts[0], id, parts[0]) | ||
} | ||
return nil | ||
} | ||
|
||
// For project paths, validate the structure | ||
if len(parts) > 2 { | ||
if parts[2] != "iap_web" { | ||
return fmt.Errorf("invalid project IAP settings path %q: third segment must be 'iap_web', got %q", id, parts[2]) | ||
} | ||
} | ||
|
||
switch len(parts) { | ||
case 2: // projects/{project_id} | ||
return nil | ||
case 3: // projects/{project_id}/iap_web | ||
return nil | ||
case 4: // projects/{project_id}/iap_web/compute or compute-{region} | ||
if !strings.HasPrefix(parts[3], "compute") && !strings.HasPrefix(parts[3], "appengine-") { | ||
return fmt.Errorf("invalid IAP web resource type %q: must start with 'compute' or 'appengine-'", parts[3]) | ||
} | ||
case 6: // projects/{project_id}/iap_web/(compute|compute-{region}|appengine-{app_id})/service/{service_id} | ||
if parts[4] != "service" { | ||
return fmt.Errorf("invalid service path %q: fifth segment must be 'service', got %q", id, parts[4]) | ||
} | ||
case 8: // projects/{project_id}/iap_web/appengine-{app_id}/service/{service_id}/version/{version_id} | ||
if !strings.HasPrefix(parts[3], "appengine-") { | ||
return fmt.Errorf("invalid path %q: version paths are only valid for App Engine resources (must start with 'appengine-')", id) | ||
} | ||
if parts[4] != "service" || parts[6] != "version" { | ||
return fmt.Errorf("invalid App Engine version path %q: must follow pattern 'appengine-{app_id}/service/{service_id}/version/{version_id}'", id) | ||
} | ||
default: | ||
return fmt.Errorf("invalid number of path segments in IAP settings ID %q: got %d segments, expected 2, 3, 4, 6, or 8", id, len(parts)) | ||
} | ||
|
||
return 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,85 @@ | ||
// Copyright 2024 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 v1alpha1 | ||
|
||
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 = &IAPSettingsRef{} | ||
|
||
// IAPSettingsRef defines the resource reference to IAPSettings, which "External" field | ||
// holds the GCP identifier for the KRM object. | ||
type IAPSettingsRef struct { | ||
// A reference to an externally managed IAPSettings resource. | ||
External string `json:"external,omitempty"` | ||
|
||
// The name of a IAPSettings resource. | ||
Name string `json:"name,omitempty"` | ||
|
||
// The namespace of a IAPSettings resource. | ||
Namespace string `json:"namespace,omitempty"` | ||
} | ||
|
||
// NormalizedExternal provision the "External" value for other resource that depends on IAPSettings. | ||
// If the "External" is given in the other resource's spec.IAPSettingsRef, the given value will be used. | ||
// Otherwise, the "Name" and "Namespace" will be used to query the actual IAPSettings object from the cluster. | ||
func (r *IAPSettingsRef) 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", IAPSettingsGVK.Kind) | ||
} | ||
// From given External | ||
if r.External != "" { | ||
if err := ValidateIAPSettingsID(r.External); err != nil { | ||
return "", fmt.Errorf("invalid format of IAPSettings external %s: %w", r.External, 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(IAPSettingsGVK) | ||
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", IAPSettingsGVK, 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) | ||
} | ||
if err := ValidateIAPSettingsID(actualExternalRef); err != nil { | ||
return "", fmt.Errorf("invalid format of IAPSettings status.externalRef %s: %w", actualExternalRef, err) | ||
} | ||
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,112 @@ | ||
// Copyright 2024 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 v1alpha1 | ||
|
||
import ( | ||
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/apis/k8s/v1alpha1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
) | ||
|
||
var IAPSettingsGVK = GroupVersion.WithKind("IAPSettings") | ||
|
||
// IAPSettingsSpec defines the desired state of IAPSettings | ||
// +kcc:proto=google.cloud.iap.v1.IapSettings | ||
type IAPSettingsSpec struct { | ||
// The IAPSettings name. | ||
ResourceID *string `json:"resourceID,omitempty"` | ||
|
||
// Required. The resource name of the IAP protected resource. | ||
// The name could have the following format: | ||
// organizations/{organization_id} | ||
// folders/{folder_id} | ||
// projects/{projects_id} | ||
// projects/{projects_id}/iap_web | ||
// projects/{projects_id}/iap_web/compute | ||
// projects/{projects_id}/iap_web/compute-{region} | ||
// projects/{projects_id}/iap_web/compute/service/{service_id} | ||
// projects/{projects_id}/iap_web/compute-{region}/service/{service_id} | ||
// projects/{projects_id}/iap_web/appengine-{app_id} | ||
// projects/{projects_id}/iap_web/appengine-{app_id}/service/{service_id} | ||
// projects/{projects_id}/iap_web/appengine-{app_id}/service/{service_id}/version/{version_id} | ||
// +kcc:proto:field=google.cloud.iap.v1.IapSettings.name | ||
// +required | ||
Name *string `json:"name,omitempty"` | ||
|
||
// Top level wrapper for all access related setting in IAP | ||
// +kcc:proto:field=google.cloud.iap.v1.IapSettings.access_settings | ||
AccessSettings *AccessSettings `json:"accessSettings,omitempty"` | ||
|
||
// Top level wrapper for all application related settings in IAP | ||
// +kcc:proto:field=google.cloud.iap.v1.IapSettings.application_settings | ||
ApplicationSettings *ApplicationSettings `json:"applicationSettings,omitempty"` | ||
} | ||
|
||
// IAPSettingsStatus defines the config connector machine state of IAPSettings | ||
type IAPSettingsStatus struct { | ||
/* Conditions represent the latest available observations of the | ||
object's current state. */ | ||
Conditions []v1alpha1.Condition `json:"conditions,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"` | ||
|
||
// A unique specifier for the IAPSettings resource in GCP. | ||
ExternalRef *string `json:"externalRef,omitempty"` | ||
|
||
// ObservedState is the state of the resource as most recently observed in GCP. | ||
// NOTYET: there is no output only field | ||
// ObservedState *IAPSettingsObservedState `json:"observedState,omitempty"` | ||
} | ||
|
||
// IAPSettingsSpec defines the desired state of IAPSettings | ||
// +kcc:proto=google.cloud.iap.v1.IapSettings | ||
// IAPSettingsObservedState is the state of the IAPSettings resource as most recently observed in GCP. | ||
// NOTYET: there is no output only field | ||
// type IAPSettingsObservedState struct { | ||
// } | ||
|
||
// +genclient | ||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object | ||
// TODO(user): make sure the pluralizaiton below is correct | ||
// +kubebuilder:resource:categories=gcp,shortName=gcpiapsettings;gcpiapsettingss | ||
// +kubebuilder:subresource:status | ||
// +kubebuilder:metadata:labels="cnrm.cloud.google.com/managed-by-kcc=true";"cnrm.cloud.google.com/system=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'" | ||
|
||
// IAPSettings is the Schema for the IAPSettings API | ||
// +k8s:openapi-gen=true | ||
type IAPSettings struct { | ||
metav1.TypeMeta `json:",inline"` | ||
metav1.ObjectMeta `json:"metadata,omitempty"` | ||
|
||
// +required | ||
Spec IAPSettingsSpec `json:"spec,omitempty"` | ||
Status IAPSettingsStatus `json:"status,omitempty"` | ||
} | ||
|
||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object | ||
// IAPSettingsList contains a list of IAPSettings | ||
type IAPSettingsList struct { | ||
metav1.TypeMeta `json:",inline"` | ||
metav1.ListMeta `json:"metadata,omitempty"` | ||
Items []IAPSettings `json:"items"` | ||
} | ||
|
||
func init() { | ||
SchemeBuilder.Register(&IAPSettings{}, &IAPSettingsList{}) | ||
} |
Oops, something went wrong.