Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
21 changes: 21 additions & 0 deletions .github/workflows/ci-presubmit.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions apis/hypercomputecluster/v1alpha1/doc.go
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=google.cloud.hypercomputecluster.v1
package v1alpha1
47 changes: 47 additions & 0 deletions apis/hypercomputecluster/v1alpha1/generate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/bin/bash
# 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.

set -o errexit
set -o nounset
set -o pipefail

REPO_ROOT="$(git rev-parse --show-toplevel)"
source "${REPO_ROOT}/dev/tools/goimports.sh"
cd ${REPO_ROOT}/dev/tools/controllerbuilder

# We need a newer googleapis to get google.cloud.hypercomputecluster.v1
PROTO_SHA="95de37fafded89761dd958268242904a6d893eae"
PROTO_OUT="${REPO_ROOT}/.build/googleapis-${PROTO_SHA}.pb"

# Unset SKIP_GENERATE_PROTOS so this specific script fetches the newer proto
OLD_SKIP_GENERATE_PROTOS="${SKIP_GENERATE_PROTOS:-}"
unset SKIP_GENERATE_PROTOS

./generate-proto.sh ${PROTO_SHA} ${PROTO_OUT}

# Restore SKIP_GENERATE_PROTOS
if [[ -n "${OLD_SKIP_GENERATE_PROTOS}" ]]; then
export SKIP_GENERATE_PROTOS="${OLD_SKIP_GENERATE_PROTOS}"
fi

go run . generate-types \
--service google.cloud.hypercomputecluster.v1 \
--api-version hypercomputecluster.cnrm.cloud.google.com/v1alpha1 \
--include-skipped-output \
--resource HypercomputeClusterCluster:Cluster \
--proto-source-path ${PROTO_OUT}

cd ${REPO_ROOT}
dev/tasks/generate-crds
33 changes: 33 additions & 0 deletions apis/hypercomputecluster/v1alpha1/groupversion_info.go
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=hypercomputecluster.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: "hypercomputecluster.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
)
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 (
"context"
"fmt"

"github.com/GoogleCloudPlatform/k8s-config-connector/apis/common"
"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 = &HypercomputeClusterClusterIdentity{}
_ identity.Resource = &HypercomputeClusterCluster{}
)

var HypercomputeClusterClusterIdentityFormat = gcpurls.Template[HypercomputeClusterClusterIdentity]("hypercomputecluster.googleapis.com", "projects/{project}/locations/{location}/clusters/{cluster}")

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

func (i *HypercomputeClusterClusterIdentity) String() string {
return HypercomputeClusterClusterIdentityFormat.ToString(*i)
}

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

*i = *parsed
return nil
}

func (i *HypercomputeClusterClusterIdentity) Host() string {
return HypercomputeClusterClusterIdentityFormat.Host()
}

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

func getIdentityFromHypercomputeClusterClusterSpec(ctx context.Context, reader client.Reader, obj *HypercomputeClusterCluster) (*HypercomputeClusterClusterIdentity, 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 := &HypercomputeClusterClusterIdentity{
Project: projectID,
Location: location,
Cluster: resourceID,
}
return identity, nil
}

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

// Cross-check the identity against the status value, if present.
externalRef := common.ValueOf(obj.Status.ExternalRef)
if externalRef != "" {
// Validate desired with actual
statusIdentity := &HypercomputeClusterClusterIdentity{}
if err := statusIdentity.FromExternal(externalRef); err != nil {
return nil, err
}

if statusIdentity.String() != specIdentity.String() {
return nil, fmt.Errorf("cannot change HypercomputeClusterCluster identity (old=%q, new=%q)", statusIdentity.String(), specIdentity.String())
}
}

return specIdentity, 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/google/go-cmp/cmp"
)

func TestHypercomputeClusterClusterIdentity_FromExternal(t *testing.T) {
tests := []struct {
name string
ref string
wantErr bool
want *HypercomputeClusterClusterIdentity
}{
{
name: "valid reference",
ref: "projects/my-project/locations/us-central1/clusters/my-cluster",
want: &HypercomputeClusterClusterIdentity{
Project: "my-project",
Location: "us-central1",
Cluster: "my-cluster",
},
},
{
name: "invalid reference format",
ref: "invalid/format",
wantErr: true,
},
{
name: "full url",
ref: "https://hypercomputecluster.googleapis.com/projects/my-project/locations/us-central1/clusters/my-cluster",
want: &HypercomputeClusterClusterIdentity{
Project: "my-project",
Location: "us-central1",
Cluster: "my-cluster",
},
},
}

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