Skip to content

Commit d2afb65

Browse files
committed
Greenfield: Implement direct KRM types, identity, and generate.sh for CCInsightsQaScorecard
Implement direct KRM types, identity, reference, and generate.sh configuration for CCInsightsQaScorecard. Issue: 11403
1 parent 62b602a commit d2afb65

20 files changed

Lines changed: 1209 additions & 0 deletions
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 = &CCInsightsQaScorecardIdentity{}
30+
_ identity.Resource = &CCInsightsQaScorecard{}
31+
)
32+
33+
var CCInsightsQaScorecardIdentityFormat = gcpurls.Template[CCInsightsQaScorecardIdentity]("contactcenterinsights.googleapis.com", "projects/{project}/locations/{location}/qaScorecards/{qaScorecard}")
34+
35+
// CCInsightsQaScorecardIdentity is the identity of a GCP CCInsightsQaScorecard resource.
36+
// +k8s:deepcopy-gen=false
37+
type CCInsightsQaScorecardIdentity struct {
38+
Project string
39+
Location string
40+
QaScorecard string
41+
}
42+
43+
func (i *CCInsightsQaScorecardIdentity) String() string {
44+
return CCInsightsQaScorecardIdentityFormat.ToString(*i)
45+
}
46+
47+
func (i *CCInsightsQaScorecardIdentity) FromExternal(ref string) error {
48+
parsed, match, err := CCInsightsQaScorecardIdentityFormat.Parse(ref)
49+
if err != nil {
50+
return fmt.Errorf("format of CCInsightsQaScorecard external=%q was not known (use %s): %w", ref, CCInsightsQaScorecardIdentityFormat.CanonicalForm(), err)
51+
}
52+
if !match {
53+
return fmt.Errorf("format of CCInsightsQaScorecard external=%q was not known (use %s)", ref, CCInsightsQaScorecardIdentityFormat.CanonicalForm())
54+
}
55+
56+
*i = *parsed
57+
return nil
58+
}
59+
60+
func (i *CCInsightsQaScorecardIdentity) Host() string {
61+
return CCInsightsQaScorecardIdentityFormat.Host()
62+
}
63+
64+
func (i *CCInsightsQaScorecardIdentity) ParentString() string {
65+
return "projects/" + i.Project + "/locations/" + i.Location
66+
}
67+
68+
func getIdentityFromCCInsightsQaScorecardSpec(ctx context.Context, reader client.Reader, obj *CCInsightsQaScorecard) (*CCInsightsQaScorecardIdentity, 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 := &CCInsightsQaScorecardIdentity{
85+
Project: projectID,
86+
Location: location,
87+
QaScorecard: resourceID,
88+
}
89+
return identity, nil
90+
}
91+
92+
func (obj *CCInsightsQaScorecard) GetIdentity(ctx context.Context, reader client.Reader) (identity.Identity, error) {
93+
specIdentity, err := getIdentityFromCCInsightsQaScorecardSpec(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 := &CCInsightsQaScorecardIdentity{}
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 CCInsightsQaScorecard identity (old=%q, new=%q)", statusIdentity.String(), specIdentity.String())
109+
}
110+
}
111+
112+
return specIdentity, nil
113+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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 TestCCInsightsQaScorecardIdentity_FromExternal(t *testing.T) {
24+
tests := []struct {
25+
name string
26+
input string
27+
expected *CCInsightsQaScorecardIdentity
28+
hasError bool
29+
}{
30+
{
31+
name: "Full resource name",
32+
input: "projects/my-project/locations/us-central1/qaScorecards/my-scorecard",
33+
expected: &CCInsightsQaScorecardIdentity{
34+
Project: "my-project",
35+
Location: "us-central1",
36+
QaScorecard: "my-scorecard",
37+
},
38+
hasError: false,
39+
},
40+
{
41+
name: "Full resource name with host",
42+
input: "contactcenterinsights.googleapis.com/projects/my-project/locations/us-central1/qaScorecards/my-scorecard",
43+
expected: &CCInsightsQaScorecardIdentity{
44+
Project: "my-project",
45+
Location: "us-central1",
46+
QaScorecard: "my-scorecard",
47+
},
48+
hasError: false,
49+
},
50+
{
51+
name: "Invalid format",
52+
input: "projects/my-project/locations/us-central1/invalid/my-scorecard",
53+
expected: nil,
54+
hasError: true,
55+
},
56+
}
57+
58+
for _, tc := range tests {
59+
t.Run(tc.name, func(t *testing.T) {
60+
id := &CCInsightsQaScorecardIdentity{}
61+
err := id.FromExternal(tc.input)
62+
if tc.hasError {
63+
if err == nil {
64+
t.Fatal("expected error, got nil")
65+
}
66+
return
67+
}
68+
if err != nil {
69+
t.Fatalf("unexpected error: %v", err)
70+
}
71+
72+
if diff := cmp.Diff(tc.expected, id); diff != "" {
73+
t.Errorf("mismatch (-want +got):\n%s", diff)
74+
}
75+
})
76+
}
77+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
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+
20+
"github.com/GoogleCloudPlatform/k8s-config-connector/apis/common"
21+
"github.com/GoogleCloudPlatform/k8s-config-connector/apis/common/identity"
22+
refs "github.com/GoogleCloudPlatform/k8s-config-connector/apis/refs/v1beta1"
23+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
24+
"k8s.io/apimachinery/pkg/runtime/schema"
25+
"k8s.io/apimachinery/pkg/types"
26+
"sigs.k8s.io/controller-runtime/pkg/client"
27+
)
28+
29+
var _ refs.Ref = &CCInsightsQaScorecardRef{}
30+
31+
// CCInsightsQaScorecardRef is a reference to a CCInsightsQaScorecard.
32+
type CCInsightsQaScorecardRef struct {
33+
// A reference to an externally managed CCInsightsQaScorecard resource.
34+
// Should be in the format "projects/{{projectID}}/locations/{{location}}/qaScorecards/{{qaScorecardID}}".
35+
External string `json:"external,omitempty"`
36+
37+
// The name of a CCInsightsQaScorecard resource.
38+
Name string `json:"name,omitempty"`
39+
40+
// The namespace of a CCInsightsQaScorecard resource.
41+
Namespace string `json:"namespace,omitempty"`
42+
}
43+
44+
func init() {
45+
refs.Register(&CCInsightsQaScorecardRef{}, &CCInsightsQaScorecard{})
46+
}
47+
48+
func (r *CCInsightsQaScorecardRef) GetGVK() schema.GroupVersionKind {
49+
return CCInsightsQaScorecardGVK
50+
}
51+
52+
func (r *CCInsightsQaScorecardRef) GetNamespacedName() types.NamespacedName {
53+
return types.NamespacedName{
54+
Name: r.Name,
55+
Namespace: r.Namespace,
56+
}
57+
}
58+
59+
func (r *CCInsightsQaScorecardRef) GetExternal() string {
60+
return r.External
61+
}
62+
63+
func (r *CCInsightsQaScorecardRef) SetExternal(ref string) {
64+
r.External = ref
65+
}
66+
67+
func (r *CCInsightsQaScorecardRef) ValidateExternal(ref string) error {
68+
id := &CCInsightsQaScorecardIdentity{}
69+
if err := id.FromExternal(ref); err != nil {
70+
return err
71+
}
72+
return nil
73+
}
74+
75+
func (r *CCInsightsQaScorecardRef) ParseExternalToIdentity() (identity.Identity, error) {
76+
id := &CCInsightsQaScorecardIdentity{}
77+
if err := id.FromExternal(r.External); err != nil {
78+
return nil, err
79+
}
80+
return id, nil
81+
}
82+
83+
func (r *CCInsightsQaScorecardRef) Normalize(ctx context.Context, reader client.Reader, defaultNamespace string) error {
84+
fallback := func(u *unstructured.Unstructured) string {
85+
obj, err := common.ToStructuredType[*CCInsightsQaScorecard](u)
86+
if err != nil {
87+
return ""
88+
}
89+
identity, err := getIdentityFromCCInsightsQaScorecardSpec(ctx, reader, obj)
90+
if err != nil {
91+
return ""
92+
}
93+
return identity.String()
94+
}
95+
return refs.NormalizeWithFallback(ctx, reader, r, defaultNamespace, fallback)
96+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
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+
refsv1beta1 "github.com/GoogleCloudPlatform/k8s-config-connector/apis/refs/v1beta1"
19+
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/apis/k8s/v1alpha1"
20+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21+
)
22+
23+
var CCInsightsQaScorecardGVK = GroupVersion.WithKind("CCInsightsQaScorecard")
24+
25+
// CCInsightsQaScorecardSpec defines the desired state of CCInsightsQaScorecard
26+
// +kcc:spec:proto=google.cloud.contactcenterinsights.v1.QaScorecard
27+
type CCInsightsQaScorecardSpec struct {
28+
// The project that this resource belongs to.
29+
ProjectRef *refsv1beta1.ProjectRef `json:"projectRef"`
30+
31+
// The location of this resource.
32+
Location string `json:"location"`
33+
34+
// The CCInsightsQaScorecard name. If not given, the metadata.name will be used.
35+
ResourceID *string `json:"resourceID,omitempty"`
36+
37+
// The user-specified display name of the scorecard.
38+
// +kcc:proto:field=google.cloud.contactcenterinsights.v1.QaScorecard.display_name
39+
// +kubebuilder:validation:Optional
40+
DisplayName *string `json:"displayName,omitempty"`
41+
42+
// A text description explaining the intent of the scorecard.
43+
// +kcc:proto:field=google.cloud.contactcenterinsights.v1.QaScorecard.description
44+
// +kubebuilder:validation:Optional
45+
Description *string `json:"description,omitempty"`
46+
}
47+
48+
// CCInsightsQaScorecardStatus defines the config connector machine state of CCInsightsQaScorecard
49+
type CCInsightsQaScorecardStatus struct {
50+
/* Conditions represent the latest available observations of the
51+
object's current state. */
52+
Conditions []v1alpha1.Condition `json:"conditions,omitempty"`
53+
54+
// 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.
55+
ObservedGeneration *int64 `json:"observedGeneration,omitempty"`
56+
57+
// A unique specifier for the CCInsightsQaScorecard resource in GCP.
58+
ExternalRef *string `json:"externalRef,omitempty"`
59+
60+
// ObservedState is the state of the resource as most recently observed in GCP.
61+
ObservedState *CCInsightsQaScorecardObservedState `json:"observedState,omitempty"`
62+
}
63+
64+
// CCInsightsQaScorecardObservedState is the state of the CCInsightsQaScorecard resource as most recently observed in GCP.
65+
// +kcc:observedstate:proto=google.cloud.contactcenterinsights.v1.QaScorecard
66+
type CCInsightsQaScorecardObservedState struct {
67+
// Output only. The time at which this scorecard was created.
68+
// +kcc:proto:field=google.cloud.contactcenterinsights.v1.QaScorecard.create_time
69+
CreateTime *string `json:"createTime,omitempty"`
70+
71+
// Output only. The most recent time at which the scorecard was updated.
72+
// +kcc:proto:field=google.cloud.contactcenterinsights.v1.QaScorecard.update_time
73+
UpdateTime *string `json:"updateTime,omitempty"`
74+
}
75+
76+
// +genclient
77+
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
78+
// +kubebuilder:resource:categories=gcp,shortName=gcpccinsightsqascorecard;gcpccinsightsqascorecards
79+
// +kubebuilder:subresource:status
80+
// +kubebuilder:metadata:labels="cnrm.cloud.google.com/managed-by-kcc=true"
81+
// +kubebuilder:metadata:labels="cnrm.cloud.google.com/system=true"
82+
// +kubebuilder:metadata:labels="cnrm.cloud.google.com/stability-level=alpha"
83+
// +kubebuilder:printcolumn:name="Age",JSONPath=".metadata.creationTimestamp",type="date"
84+
// +kubebuilder:printcolumn:name="Ready",JSONPath=".status.conditions[?(@.type=='Ready')].status",type="string",description="When 'True', the most recent reconcile of the resource succeeded"
85+
// +kubebuilder:printcolumn:name="Status",JSONPath=".status.conditions[?(@.type=='Ready')].reason",type="string",description="The reason for the value in 'Ready'"
86+
// +kubebuilder:printcolumn:name="Status Age",JSONPath=".status.conditions[?(@.type=='Ready')].lastTransitionTime",type="date",description="The last transition time for the value in 'Status'"
87+
88+
// CCInsightsQaScorecard is the Schema for the CCInsightsQaScorecard API
89+
// +k8s:openapi-gen=true
90+
type CCInsightsQaScorecard struct {
91+
metav1.TypeMeta `json:",inline"`
92+
metav1.ObjectMeta `json:"metadata,omitempty"`
93+
94+
// +required
95+
Spec CCInsightsQaScorecardSpec `json:"spec,omitempty"`
96+
Status CCInsightsQaScorecardStatus `json:"status,omitempty"`
97+
}
98+
99+
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
100+
// CCInsightsQaScorecardList contains a list of CCInsightsQaScorecard
101+
type CCInsightsQaScorecardList struct {
102+
metav1.TypeMeta `json:",inline"`
103+
metav1.ListMeta `json:"metadata,omitempty"`
104+
Items []CCInsightsQaScorecard `json:"items"`
105+
}
106+
107+
func init() {
108+
SchemeBuilder.Register(&CCInsightsQaScorecard{}, &CCInsightsQaScorecardList{})
109+
}

0 commit comments

Comments
 (0)