Skip to content

Commit 137ae84

Browse files
feat(hypercomputecluster): add HypercomputeClusterCluster Greenfield API and identity
Implement KRM types, identity, reference, and generate.sh for the greenfield HypercomputeClusterCluster resource under group hypercomputecluster.cnrm.cloud.google.com/v1alpha1. Fixes #11157
1 parent 6ca1c1c commit 137ae84

38 files changed

Lines changed: 5656 additions & 0 deletions

.github/workflows/ci-presubmit.yaml

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// +kcc:proto=google.cloud.hypercomputecluster.v1
16+
package v1alpha1
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/bin/bash
2+
# Copyright 2026 Google LLC
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
set -o errexit
17+
set -o nounset
18+
set -o pipefail
19+
20+
REPO_ROOT="$(git rev-parse --show-toplevel)"
21+
source "${REPO_ROOT}/dev/tools/goimports.sh"
22+
cd ${REPO_ROOT}/dev/tools/controllerbuilder
23+
24+
# We need a newer googleapis to get google.cloud.hypercomputecluster.v1
25+
PROTO_SHA="95de37fafded89761dd958268242904a6d893eae"
26+
PROTO_OUT="${REPO_ROOT}/.build/googleapis-${PROTO_SHA}.pb"
27+
28+
# Unset SKIP_GENERATE_PROTOS so this specific script fetches the newer proto
29+
OLD_SKIP_GENERATE_PROTOS="${SKIP_GENERATE_PROTOS:-}"
30+
unset SKIP_GENERATE_PROTOS
31+
32+
./generate-proto.sh ${PROTO_SHA} ${PROTO_OUT}
33+
34+
# Restore SKIP_GENERATE_PROTOS
35+
if [[ -n "${OLD_SKIP_GENERATE_PROTOS}" ]]; then
36+
export SKIP_GENERATE_PROTOS="${OLD_SKIP_GENERATE_PROTOS}"
37+
fi
38+
39+
go run . generate-types \
40+
--service google.cloud.hypercomputecluster.v1 \
41+
--api-version hypercomputecluster.cnrm.cloud.google.com/v1alpha1 \
42+
--include-skipped-output \
43+
--resource HypercomputeClusterCluster:Cluster \
44+
--proto-source-path ${PROTO_OUT}
45+
46+
cd ${REPO_ROOT}
47+
dev/tasks/generate-crds
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// +kubebuilder:object:generate=true
16+
// +groupName=hypercomputecluster.cnrm.cloud.google.com
17+
package v1alpha1
18+
19+
import (
20+
"k8s.io/apimachinery/pkg/runtime/schema"
21+
"sigs.k8s.io/controller-runtime/pkg/scheme"
22+
)
23+
24+
var (
25+
// GroupVersion is group version used to register these objects
26+
GroupVersion = schema.GroupVersion{Group: "hypercomputecluster.cnrm.cloud.google.com", Version: "v1alpha1"}
27+
28+
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
29+
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
30+
31+
// AddToScheme adds the types in this group-version to the given scheme.
32+
AddToScheme = SchemeBuilder.AddToScheme
33+
)
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package v1alpha1
16+
17+
import (
18+
"context"
19+
"fmt"
20+
21+
"github.com/GoogleCloudPlatform/k8s-config-connector/apis/common"
22+
"github.com/GoogleCloudPlatform/k8s-config-connector/apis/common/identity"
23+
refs "github.com/GoogleCloudPlatform/k8s-config-connector/apis/refs/v1beta1"
24+
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/gcpurls"
25+
"sigs.k8s.io/controller-runtime/pkg/client"
26+
)
27+
28+
var (
29+
_ identity.IdentityV2 = &HypercomputeClusterClusterIdentity{}
30+
_ identity.Resource = &HypercomputeClusterCluster{}
31+
)
32+
33+
var HypercomputeClusterClusterIdentityFormat = gcpurls.Template[HypercomputeClusterClusterIdentity]("hypercomputecluster.googleapis.com", "projects/{project}/locations/{location}/clusters/{cluster}")
34+
35+
// HypercomputeClusterClusterIdentity is the identity of a GCP HypercomputeClusterCluster resource.
36+
// +k8s:deepcopy-gen=false
37+
type HypercomputeClusterClusterIdentity struct {
38+
Project string
39+
Location string
40+
Cluster string
41+
}
42+
43+
func (i *HypercomputeClusterClusterIdentity) String() string {
44+
return HypercomputeClusterClusterIdentityFormat.ToString(*i)
45+
}
46+
47+
func (i *HypercomputeClusterClusterIdentity) FromExternal(ref string) error {
48+
parsed, match, err := HypercomputeClusterClusterIdentityFormat.Parse(ref)
49+
if err != nil {
50+
return fmt.Errorf("format of HypercomputeClusterCluster external=%q was not known (use %s): %w", ref, HypercomputeClusterClusterIdentityFormat.CanonicalForm(), err)
51+
}
52+
if !match {
53+
return fmt.Errorf("format of HypercomputeClusterCluster external=%q was not known (use %s)", ref, HypercomputeClusterClusterIdentityFormat.CanonicalForm())
54+
}
55+
56+
*i = *parsed
57+
return nil
58+
}
59+
60+
func (i *HypercomputeClusterClusterIdentity) Host() string {
61+
return HypercomputeClusterClusterIdentityFormat.Host()
62+
}
63+
64+
func (i *HypercomputeClusterClusterIdentity) ParentString() string {
65+
return fmt.Sprintf("projects/%s/locations/%s", i.Project, i.Location)
66+
}
67+
68+
func getIdentityFromHypercomputeClusterClusterSpec(ctx context.Context, reader client.Reader, obj *HypercomputeClusterCluster) (*HypercomputeClusterClusterIdentity, error) {
69+
resourceID, err := refs.GetResourceID(obj)
70+
if err != nil {
71+
return nil, fmt.Errorf("cannot resolve resource ID: %w", err)
72+
}
73+
74+
location, err := refs.GetLocation(obj)
75+
if err != nil {
76+
return nil, fmt.Errorf("cannot resolve location: %w", err)
77+
}
78+
79+
projectID, err := refs.ResolveProjectID(ctx, reader, obj)
80+
if err != nil {
81+
return nil, fmt.Errorf("cannot resolve project: %w", err)
82+
}
83+
84+
identity := &HypercomputeClusterClusterIdentity{
85+
Project: projectID,
86+
Location: location,
87+
Cluster: resourceID,
88+
}
89+
return identity, nil
90+
}
91+
92+
func (obj *HypercomputeClusterCluster) GetIdentity(ctx context.Context, reader client.Reader) (identity.Identity, error) {
93+
specIdentity, err := getIdentityFromHypercomputeClusterClusterSpec(ctx, reader, obj)
94+
if err != nil {
95+
return nil, err
96+
}
97+
98+
// Cross-check the identity against the status value, if present.
99+
externalRef := common.ValueOf(obj.Status.ExternalRef)
100+
if externalRef != "" {
101+
// Validate desired with actual
102+
statusIdentity := &HypercomputeClusterClusterIdentity{}
103+
if err := statusIdentity.FromExternal(externalRef); err != nil {
104+
return nil, err
105+
}
106+
107+
if statusIdentity.String() != specIdentity.String() {
108+
return nil, fmt.Errorf("cannot change HypercomputeClusterCluster identity (old=%q, new=%q)", statusIdentity.String(), specIdentity.String())
109+
}
110+
}
111+
112+
return specIdentity, nil
113+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package v1alpha1
16+
17+
import (
18+
"testing"
19+
20+
"github.com/google/go-cmp/cmp"
21+
)
22+
23+
func TestHypercomputeClusterClusterIdentity_FromExternal(t *testing.T) {
24+
tests := []struct {
25+
name string
26+
ref string
27+
wantErr bool
28+
want *HypercomputeClusterClusterIdentity
29+
}{
30+
{
31+
name: "valid reference",
32+
ref: "projects/my-project/locations/us-central1/clusters/my-cluster",
33+
want: &HypercomputeClusterClusterIdentity{
34+
Project: "my-project",
35+
Location: "us-central1",
36+
Cluster: "my-cluster",
37+
},
38+
},
39+
{
40+
name: "invalid reference format",
41+
ref: "invalid/format",
42+
wantErr: true,
43+
},
44+
{
45+
name: "full url",
46+
ref: "https://hypercomputecluster.googleapis.com/projects/my-project/locations/us-central1/clusters/my-cluster",
47+
want: &HypercomputeClusterClusterIdentity{
48+
Project: "my-project",
49+
Location: "us-central1",
50+
Cluster: "my-cluster",
51+
},
52+
},
53+
}
54+
55+
for _, tt := range tests {
56+
t.Run(tt.name, func(t *testing.T) {
57+
i := &HypercomputeClusterClusterIdentity{}
58+
err := i.FromExternal(tt.ref)
59+
if (err != nil) != tt.wantErr {
60+
t.Errorf("FromExternal() error = %v, wantErr %v", err, tt.wantErr)
61+
return
62+
}
63+
if !tt.wantErr {
64+
if diff := cmp.Diff(tt.want, i); diff != "" {
65+
t.Errorf("FromExternal() mismatch (-want +got):\n%s", diff)
66+
}
67+
}
68+
})
69+
}
70+
}

0 commit comments

Comments
 (0)