Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Journal: NetworkSecurityUrlList

## Observations & Learnings
- **Resource Selection**: `UrlList` is a standard GCP resource under the network security service.
- **API Mapping**: The resource was configured under `apis/networksecurity/v1alpha1/generate.sh` mapping KCC kind `NetworkSecurityUrlList` to proto `UrlList`.
- **Identity Template**: The CAIS name format `//networksecurity.googleapis.com/projects/{{PROJECT_ID}}/locations/{{LOCATION}}/urlLists/{{URL_LIST}}` was successfully mapped to `projects/{project}/locations/{location}/urlLists/{urllist}` for identity verification.
- **Reference Pattern**: Implemented the reference pattern delegating `Normalize` strictly to `refs.Normalize` instead of `NormalizeWithFallback` since it is a modern direct greenfield controller.
- **Unit Tests**: Added comprehensive identity formatting/parsing tests and registered the template within `gcpurls`.
1 change: 1 addition & 0 deletions apis/networksecurity/v1alpha1/generate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ go run . generate-types \
--resource NetworkSecurityTLSInspectionPolicy:TlsInspectionPolicy \
--resource NetworkSecurityAuthzPolicy:AuthzPolicy \
--resource NetworkSecurityFirewallEndpoint:FirewallEndpoint \
--resource NetworkSecurityUrlList:UrlList \
--proto-source-path ${PROTO_OUT}

# Run for google.cloud.networksecurity.v1alpha1 resources (PartnerSSERealm)
Expand Down
106 changes: 106 additions & 0 deletions apis/networksecurity/v1alpha1/networksecurityurllist_identity.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright 2026 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"

"github.com/GoogleCloudPlatform/k8s-config-connector/apis/common/identity"
refs "github.com/GoogleCloudPlatform/k8s-config-connector/apis/refs/v1beta1"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/gcpurls"
"sigs.k8s.io/controller-runtime/pkg/client"
)

var (
_ identity.IdentityV2 = &NetworkSecurityUrlListIdentity{}
_ identity.Resource = &NetworkSecurityUrlList{}
)

var NetworkSecurityUrlListIdentityFormat = gcpurls.Template[NetworkSecurityUrlListIdentity]("networksecurity.googleapis.com", "projects/{project}/locations/{location}/urlLists/{urllist}")

// NetworkSecurityUrlListIdentity is the identity of a GCP NetworkSecurityUrlList resource.
// +k8s:deepcopy-gen=false
type NetworkSecurityUrlListIdentity struct {
Project string
Location string
UrlList string
}

func (i *NetworkSecurityUrlListIdentity) String() string {
return NetworkSecurityUrlListIdentityFormat.ToString(*i)
}

func (i *NetworkSecurityUrlListIdentity) FromExternal(ref string) error {
parsed, match, err := NetworkSecurityUrlListIdentityFormat.Parse(ref)
if err != nil {
return fmt.Errorf("format of NetworkSecurityUrlList external=%q was not known (use %s): %w", ref, NetworkSecurityUrlListIdentityFormat.CanonicalForm(), err)
}
if !match {
return fmt.Errorf("format of NetworkSecurityUrlList external=%q was not known (use %s)", ref, NetworkSecurityUrlListIdentityFormat.CanonicalForm())
}

*i = *parsed
return nil
}

func (i *NetworkSecurityUrlListIdentity) Host() string {
return NetworkSecurityUrlListIdentityFormat.Host()
}

func (i *NetworkSecurityUrlListIdentity) ParentString() string {
return fmt.Sprintf("projects/%s/locations/%s", i.Project, i.Location)
}

func getIdentityFromNetworkSecurityUrlListSpec(ctx context.Context, reader client.Reader, obj *NetworkSecurityUrlList) (*NetworkSecurityUrlListIdentity, error) {
resourceID, err := refs.GetResourceID(obj)
if err != nil {
return nil, fmt.Errorf("cannot resolve resource ID: %w", err)
}

location, err := refs.GetLocation(obj)
if err != nil {
return nil, fmt.Errorf("cannot resolve location: %w", err)
}

projectID, err := refs.ResolveProjectID(ctx, reader, obj)
if err != nil {
return nil, fmt.Errorf("cannot resolve project: %w", err)
}

identity := &NetworkSecurityUrlListIdentity{
Project: projectID,
Location: location,
UrlList: resourceID,
}
return identity, nil
}

func (obj *NetworkSecurityUrlList) GetIdentity(ctx context.Context, reader client.Reader) (identity.Identity, error) {
specIdentity, err := getIdentityFromNetworkSecurityUrlListSpec(ctx, reader, obj)
if err != nil {
return nil, err
}

return specIdentity, nil
}

// ExternalIdentifier returns the GCP external identifier (the GCP URL).
func (obj *NetworkSecurityUrlList) ExternalIdentifier() *string {
if obj.Status.ExternalRef != nil {
return obj.Status.ExternalRef
}
return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright 2026 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 (
"testing"

"github.com/GoogleCloudPlatform/k8s-config-connector/apis/common/identity"
"github.com/google/go-cmp/cmp"
)

func TestNetworkSecurityUrlListIdentity(t *testing.T) {
tests := []struct {
name string
external string
want *NetworkSecurityUrlListIdentity
wantErr bool
}{
{
name: "basic",
external: "projects/my-project/locations/us-central1/urlLists/my-urllist",
want: &NetworkSecurityUrlListIdentity{
Project: "my-project",
Location: "us-central1",
UrlList: "my-urllist",
},
},
{
name: "invalid format",
external: "invalid/my-project/locations/us-central1/urlLists/my-urllist",
wantErr: true,
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
got := &NetworkSecurityUrlListIdentity{}
err := got.FromExternal(tc.external)
if (err != nil) != tc.wantErr {
t.Errorf("FromExternal() error = %v, wantErr %v", err, tc.wantErr)
return
}
if !tc.wantErr {
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("FromExternal() mismatch (-want +got):\n%s", diff)
}
if got.String() != tc.external {
t.Errorf("String() = %v, want %v", got.String(), tc.external)
}
}
})
}
}

func TestNetworkSecurityUrlListIdentity_Interfaces(t *testing.T) {
var _ identity.IdentityV2 = &NetworkSecurityUrlListIdentity{}
var _ identity.Resource = &NetworkSecurityUrlList{}
}
85 changes: 85 additions & 0 deletions apis/networksecurity/v1alpha1/networksecurityurllist_reference.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright 2026 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"

"github.com/GoogleCloudPlatform/k8s-config-connector/apis/common/identity"
refs "github.com/GoogleCloudPlatform/k8s-config-connector/apis/refs/v1beta1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
)

var _ refs.Ref = &NetworkSecurityUrlListRef{}

// NetworkSecurityUrlListRef is a reference to a GCP NetworkSecurityUrlList.
type NetworkSecurityUrlListRef struct {
// A reference to an externally managed NetworkSecurityUrlList resource.
// Should be in the format "projects/{{projectID}}/locations/{{location}}/urlLists/{{url_list}}".
External string `json:"external,omitempty"`

// The name of a NetworkSecurityUrlList resource.
Name string `json:"name,omitempty"`

// The namespace of a NetworkSecurityUrlList resource.
Namespace string `json:"namespace,omitempty"`
}

func init() {
refs.Register(&NetworkSecurityUrlListRef{}, &NetworkSecurityUrlList{})
}

func (r *NetworkSecurityUrlListRef) GetGVK() schema.GroupVersionKind {
return NetworkSecurityUrlListGVK
}

func (r *NetworkSecurityUrlListRef) GetNamespacedName() types.NamespacedName {
return types.NamespacedName{
Name: r.Name,
Namespace: r.Namespace,
}
}

func (r *NetworkSecurityUrlListRef) GetExternal() string {
return r.External
}

func (r *NetworkSecurityUrlListRef) SetExternal(ref string) {
r.External = ref
r.Name = ""
r.Namespace = ""
}

func (r *NetworkSecurityUrlListRef) ValidateExternal(ref string) error {
id := &NetworkSecurityUrlListIdentity{}
if err := id.FromExternal(ref); err != nil {
return err
}
return nil
}

func (r *NetworkSecurityUrlListRef) ParseExternalToIdentity() (identity.Identity, error) {
id := &NetworkSecurityUrlListIdentity{}
if err := id.FromExternal(r.External); err != nil {
return nil, err
}
return id, nil
}

func (r *NetworkSecurityUrlListRef) Normalize(ctx context.Context, reader client.Reader, defaultNamespace string) error {
return refs.Normalize(ctx, reader, r, defaultNamespace)
}
113 changes: 113 additions & 0 deletions apis/networksecurity/v1alpha1/networksecurityurllist_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright 2026 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 (
refsv1beta1 "github.com/GoogleCloudPlatform/k8s-config-connector/apis/refs/v1beta1"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/apis/k8s/v1alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var NetworkSecurityUrlListGVK = GroupVersion.WithKind("NetworkSecurityUrlList")

// NetworkSecurityUrlListSpec defines the desired state of NetworkSecurityUrlList
// +kcc:spec:proto=google.cloud.networksecurity.v1.UrlList
type NetworkSecurityUrlListSpec struct {
// The project that this resource belongs to.
ProjectRef *refsv1beta1.ProjectRef `json:"projectRef"`

// The location of this resource.
Location string `json:"location"`

// The NetworkSecurityUrlList name. If not given, the metadata.name will be used.
ResourceID *string `json:"resourceID,omitempty"`

// Optional. Free-text description of the resource.
// +kcc:proto:field=google.cloud.networksecurity.v1.UrlList.description
Description *string `json:"description,omitempty"`

// Required. The list of values that make up this resource.
// Each value can be a host, a host pattern, a URL, or a URL pattern.
// +kubebuilder:validation:Required
// +kcc:proto:field=google.cloud.networksecurity.v1.UrlList.values
Values []string `json:"values"`

// Optional. Set of labels to organize your UrlList.
// +kcc:proto:field=google.cloud.networksecurity.v1.UrlList.labels
Labels map[string]string `json:"labels,omitempty"`
}

// NetworkSecurityUrlListStatus defines the config connector machine state of NetworkSecurityUrlList
type NetworkSecurityUrlListStatus 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 NetworkSecurityUrlList resource in GCP.
ExternalRef *string `json:"externalRef,omitempty"`

// ObservedState is the state of the resource as most recently observed in GCP.
ObservedState *NetworkSecurityUrlListObservedState `json:"observedState,omitempty"`
}

// NetworkSecurityUrlListObservedState is the state of the NetworkSecurityUrlList resource as most recently observed in GCP.
// +kcc:observedstate:proto=google.cloud.networksecurity.v1.UrlList
type NetworkSecurityUrlListObservedState struct {
// Output only. The timestamp when the resource was created.
// +kcc:proto:field=google.cloud.networksecurity.v1.UrlList.create_time
CreateTime *string `json:"createTime,omitempty"`

// Output only. The timestamp when the resource was last updated.
// +kcc:proto:field=google.cloud.networksecurity.v1.UrlList.update_time
UpdateTime *string `json:"updateTime,omitempty"`
}

// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +kubebuilder:resource:categories=gcp,shortName=gcpnetworksecurityurllist;gcpnetworksecurityurllists
// +kubebuilder:subresource:status
// +kubebuilder:metadata:labels="cnrm.cloud.google.com/managed-by-kcc=true"
// +kubebuilder:metadata:labels="cnrm.cloud.google.com/system=true"
// +kubebuilder:metadata:labels="cnrm.cloud.google.com/stability-level=alpha"
// +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'"

// NetworkSecurityUrlList is the Schema for the NetworkSecurityUrlList API
// +k8s:openapi-gen=true
type NetworkSecurityUrlList struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

// +required
Spec NetworkSecurityUrlListSpec `json:"spec,omitempty"`
Status NetworkSecurityUrlListStatus `json:"status,omitempty"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// NetworkSecurityUrlListList contains a list of NetworkSecurityUrlList
type NetworkSecurityUrlListList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []NetworkSecurityUrlList `json:"items"`
}

func init() {
SchemeBuilder.Register(&NetworkSecurityUrlList{}, &NetworkSecurityUrlListList{})
}
Loading
Loading