diff --git a/apis/common/projects/mapper.go b/apis/common/projects/mapper.go new file mode 100644 index 0000000000..bec6538b7a --- /dev/null +++ b/apis/common/projects/mapper.go @@ -0,0 +1,58 @@ +package projects + +import ( + "context" + "fmt" + "strconv" + "strings" + + resourcemanager "cloud.google.com/go/resourcemanager/apiv3" + "cloud.google.com/go/resourcemanager/apiv3/resourcemanagerpb" +) + +type ProjectMapper struct { + client *resourcemanager.ProjectsClient +} + +func NewProjectMapper(client *resourcemanager.ProjectsClient) *ProjectMapper { + return &ProjectMapper{ + client: client, + } +} + +func (m *ProjectMapper) ReplaceProjectNumberWithID(ctx context.Context, projectID string) (string, error) { + if _, err := strconv.ParseInt(projectID, 10, 64); err != nil { + // Not a project number, no need to map + return projectID, nil + } + + req := &resourcemanagerpb.GetProjectRequest{ + Name: "projects/" + projectID, + } + project, err := m.client.GetProject(ctx, req) + if err != nil { + return "", fmt.Errorf("error getting project %q: %w", req.Name, err) + } + return project.ProjectId, nil +} + +func (m *ProjectMapper) LookupProjectNumber(ctx context.Context, projectID string) (int64, error) { + // Check if the project number is already a valid integer + // If not, we need to look it up + projectNumber, err := strconv.ParseInt(projectID, 10, 64) + if err != nil { + req := &resourcemanagerpb.GetProjectRequest{ + Name: "projects/" + projectID, + } + project, err := m.client.GetProject(ctx, req) + if err != nil { + return 0, fmt.Errorf("error getting project %q: %w", req.Name, err) + } + n, err := strconv.ParseInt(strings.TrimPrefix(project.Name, "projects/"), 10, 64) + if err != nil { + return 0, fmt.Errorf("error parsing project number for %q: %w", project.Name, err) + } + projectNumber = n + } + return projectNumber, nil +} diff --git a/apis/discoveryengine/v1alpha1/targetsite_reference.go b/apis/discoveryengine/v1alpha1/targetsite_reference.go new file mode 100644 index 0000000000..7961c6689e --- /dev/null +++ b/apis/discoveryengine/v1alpha1/targetsite_reference.go @@ -0,0 +1,155 @@ +// 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" + + 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 = &TargetSiteRef{} + +// TargetSiteRef defines the resource reference to DiscoveryEngineDataStoreTargetSite, which "External" field +// holds the GCP identifier for the KRM object. +type TargetSiteRef struct { + // A reference to an externally managed DiscoveryEngineDataStoreTargetSite resource. + // Should be in the format "projects//locations//targetsites/". + External string `json:"external,omitempty"` + + // The name of a DiscoveryEngineDataStoreTargetSite resource. + Name string `json:"name,omitempty"` + + // The namespace of a DiscoveryEngineDataStoreTargetSite resource. + Namespace string `json:"namespace,omitempty"` +} + +// TargetSiteLink defines the full identity for a DataStoreTargetSite +// +// +k8s:deepcopy-gen=false +type TargetSiteLink struct { + *DiscoveryEngineDataStoreID + TargetSite string +} + +func (l *TargetSiteLink) String() string { + return l.DiscoveryEngineDataStoreID.String() + "/siteSearchEngine/targetSites/" + l.TargetSite +} + +// NormalizedExternal provision the "External" value for other resource that depends on DiscoveryEngineDataStoreTargetSite. +// If the "External" is given in the other resource's spec.DiscoveryEngineDataStoreTargetSiteRef, the given value will be used. +// Otherwise, the "Name" and "Namespace" will be used to query the actual DiscoveryEngineDataStoreTargetSite object from the cluster. +func (r *TargetSiteRef) 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", DiscoveryEngineDataStoreTargetSiteGVK.Kind) + } + // From given External + if r.External != "" { + if _, err := ParseTargetSiteExternal(r.External); err != nil { + return "", 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(DiscoveryEngineDataStoreTargetSiteGVK) + 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", DiscoveryEngineDataStoreTargetSiteGVK, 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) + } + r.External = actualExternalRef + return r.External, nil +} + +// NewTargetSiteLinkFromObject builds a TargetSiteLink from the Config Connector object. +func NewTargetSiteLinkFromObject(ctx context.Context, reader client.Reader, obj *DiscoveryEngineDataStoreTargetSite) (*DiscoveryEngineDataStoreID, *TargetSiteLink, error) { + if obj.Spec.DataStoreRef == nil { + return nil, nil, fmt.Errorf("spec.dataStoreRef not set") + } + dataStoreRef := *obj.Spec.DataStoreRef + if _, err := dataStoreRef.NormalizedExternal(ctx, reader, obj.GetNamespace()); err != nil { + return nil, nil, fmt.Errorf("resolving spec.dataStoreRef: %w", err) + } + dataStoreLink, err := ParseDiscoveryEngineDataStoreExternal(dataStoreRef.External) + if err != nil { + return nil, nil, fmt.Errorf("parsing dataStoreRef.external=%q: %w", dataStoreRef.External, err) + } + + var link *TargetSiteLink + + // Validate the status.externalRef, if set + externalRef := valueOf(obj.Status.ExternalRef) + if externalRef != "" { + // Validate desired with actual + externalLink, err := ParseTargetSiteExternal(externalRef) + if err != nil { + return nil, nil, err + } + if externalLink.DiscoveryEngineDataStoreID.String() != dataStoreLink.String() { + return nil, nil, fmt.Errorf("cannot change object key after creation; status=%q, new=%q", + externalLink.DiscoveryEngineDataStoreID.String(), dataStoreLink.String()) + } + link = externalLink + } + return dataStoreLink, link, nil +} + +func ParseTargetSiteExternal(external string) (*TargetSiteLink, error) { + s := strings.TrimPrefix(external, "//discoveryengine.googleapis.com/") + s = strings.TrimPrefix(s, "/") + tokens := strings.Split(s, "/") + if len(tokens) == 11 && tokens[0] == "projects" && tokens[2] == "locations" && tokens[4] == "collections" && tokens[6] == "dataStores" && tokens[8] == "siteSearchEngine" && tokens[9] == "targetSites" { + projectAndLocation := &ProjectAndLocation{ + ProjectID: tokens[1], + Location: tokens[3], + } + collection := &CollectionLink{ + ProjectAndLocation: projectAndLocation, + Collection: tokens[5], + } + dataStoreLink := &DiscoveryEngineDataStoreID{ + CollectionLink: collection, + DataStore: tokens[7], + } + targetStoreLink := &TargetSiteLink{ + DiscoveryEngineDataStoreID: dataStoreLink, + TargetSite: tokens[10], + } + return targetStoreLink, nil + } + return nil, fmt.Errorf("format of DiscoveryEngineDataStoreTargetSite external=%q was not known (use projects/{{projectId}}/locations/{{location}}/collections/{{collectionID}}/dataStores/{{dataStoreID}}/siteSearchEngine/targetSites/{{targetSiteID}})", external) +} diff --git a/apis/discoveryengine/v1alpha1/targetsite_types.go b/apis/discoveryengine/v1alpha1/targetsite_types.go new file mode 100644 index 0000000000..ae83972482 --- /dev/null +++ b/apis/discoveryengine/v1alpha1/targetsite_types.go @@ -0,0 +1,118 @@ +// 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 DiscoveryEngineDataStoreTargetSiteGVK = GroupVersion.WithKind("DiscoveryEngineDataStoreTargetSite") + +// DiscoveryEngineDataStoreTargetSiteSpec defines the desired state of DiscoveryEngineDataStoreTargetSite +// +kcc:proto=google.cloud.discoveryengine.v1.TargetSite +type DiscoveryEngineDataStoreTargetSiteSpec struct { + // The DataStore this target site should be part of. + DataStoreRef *DiscoveryEngineDataStoreRef `json:"dataStoreRef,omitempty"` + + // The resource ID is server-generated, so no ResourceID field + + // Required. Input only. The user provided URI pattern from which the + // `generated_uri_pattern` is generated. + ProvidedUriPattern *string `json:"providedUriPattern,omitempty"` + + // The type of the target site, e.g., whether the site is to be included or + // excluded. + Type *string `json:"type,omitempty"` + + // Input only. If set to false, a uri_pattern is generated to include all + // pages whose address contains the provided_uri_pattern. If set to true, an + // uri_pattern is generated to try to be an exact match of the + // provided_uri_pattern or just the specific page if the provided_uri_pattern + // is a specific one. provided_uri_pattern is always normalized to + // generate the URI pattern to be used by the search engine. + ExactMatch *bool `json:"exactMatch,omitempty"` +} + +// DiscoveryEngineDataStoreTargetSiteStatus defines the config connector machine state of DiscoveryEngineDataStoreTargetSite +type DiscoveryEngineDataStoreTargetSiteStatus 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 DiscoveryEngineDataStoreTargetSite resource in GCP. + ExternalRef *string `json:"externalRef,omitempty"` + + // ObservedState is the state of the resource as most recently observed in GCP. + ObservedState *DiscoveryEngineDataStoreTargetSiteObservedState `json:"observedState,omitempty"` +} + +// DiscoveryEngineDataStoreTargetSiteObservedState is the state of the DiscoveryEngineDataStoreTargetSite resource as most recently observed in GCP. +// +kcc:proto=google.cloud.discoveryengine.v1.TargetSite +type DiscoveryEngineDataStoreTargetSiteObservedState struct { + // Output only. This is system-generated based on the provided_uri. + GeneratedUriPattern *string `json:"generatedUriPattern,omitempty"` + + // Output only. Root domain of the provided_uri. + RootDomainUri *string `json:"rootDomainUri,omitempty"` + + // Output only. Site ownership and validity verification status. + SiteVerificationInfo *SiteVerificationInfo `json:"siteVerificationInfo,omitempty"` + + // Output only. Indexing status. + IndexingStatus *string `json:"indexingStatus,omitempty"` + + // Output only. The target site's last updated time. + UpdateTime *string `json:"updateTime,omitempty"` + + // Output only. Failure reason. + FailureReason *TargetSite_FailureReason `json:"failureReason,omitempty"` +} + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:resource:categories=gcp,shortName=gcpdiscoveryenginedatastoretargetsite;gcpdiscoveryenginedatastoretargetsites +// +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'" + +// DiscoveryEngineDataStoreTargetSite is the Schema for the DiscoveryEngineDataStoreTargetSite API +// +k8s:openapi-gen=true +type DiscoveryEngineDataStoreTargetSite struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // +required + Spec DiscoveryEngineDataStoreTargetSiteSpec `json:"spec,omitempty"` + Status DiscoveryEngineDataStoreTargetSiteStatus `json:"status,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// DiscoveryEngineDataStoreTargetSiteList contains a list of DiscoveryEngineDataStoreTargetSite +type DiscoveryEngineDataStoreTargetSiteList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []DiscoveryEngineDataStoreTargetSite `json:"items"` +} + +func init() { + SchemeBuilder.Register(&DiscoveryEngineDataStoreTargetSite{}, &DiscoveryEngineDataStoreTargetSiteList{}) +} diff --git a/apis/discoveryengine/v1alpha1/types.generated.go b/apis/discoveryengine/v1alpha1/types.generated.go index ca40cd1282..154bcaf115 100644 --- a/apis/discoveryengine/v1alpha1/types.generated.go +++ b/apis/discoveryengine/v1alpha1/types.generated.go @@ -17,21 +17,27 @@ package v1alpha1 // +kcc:proto=google.cloud.discoveryengine.v1.DataStore.BillingEstimation type DataStore_BillingEstimation struct { // Data size for structured data in terms of bytes. + // +kcc:proto=google.cloud.discoveryengine.v1.DataStore.BillingEstimation.structured_data_size StructuredDataSize *int64 `json:"structuredDataSize,omitempty"` // Data size for unstructured data in terms of bytes. + // +kcc:proto=google.cloud.discoveryengine.v1.DataStore.BillingEstimation.unstructured_data_size UnstructuredDataSize *int64 `json:"unstructuredDataSize,omitempty"` // Data size for websites in terms of bytes. + // +kcc:proto=google.cloud.discoveryengine.v1.DataStore.BillingEstimation.website_data_size WebsiteDataSize *int64 `json:"websiteDataSize,omitempty"` // Last updated timestamp for structured data. + // +kcc:proto=google.cloud.discoveryengine.v1.DataStore.BillingEstimation.structured_data_update_time StructuredDataUpdateTime *string `json:"structuredDataUpdateTime,omitempty"` // Last updated timestamp for unstructured data. + // +kcc:proto=google.cloud.discoveryengine.v1.DataStore.BillingEstimation.unstructured_data_update_time UnstructuredDataUpdateTime *string `json:"unstructuredDataUpdateTime,omitempty"` // Last updated timestamp for websites. + // +kcc:proto=google.cloud.discoveryengine.v1.DataStore.BillingEstimation.website_data_update_time WebsiteDataUpdateTime *string `json:"websiteDataUpdateTime,omitempty"` } @@ -40,15 +46,18 @@ type DocumentProcessingConfig struct { // The full resource name of the Document Processing Config. // Format: // `projects/*/locations/*/collections/*/dataStores/*/documentProcessingConfig`. + // +kcc:proto=google.cloud.discoveryengine.v1.DocumentProcessingConfig.name Name *string `json:"name,omitempty"` // Whether chunking mode is enabled. + // +kcc:proto=google.cloud.discoveryengine.v1.DocumentProcessingConfig.chunking_config ChunkingConfig *DocumentProcessingConfig_ChunkingConfig `json:"chunkingConfig,omitempty"` // Configurations for default Document parser. // If not specified, we will configure it as default DigitalParsingConfig, and // the default parsing config will be applied to all file types for Document // parsing. + // +kcc:proto=google.cloud.discoveryengine.v1.DocumentProcessingConfig.default_parsing_config DefaultParsingConfig *DocumentProcessingConfig_ParsingConfig `json:"defaultParsingConfig,omitempty"` // TODO: map type string message for parsing_config_overrides @@ -58,6 +67,7 @@ type DocumentProcessingConfig struct { // +kcc:proto=google.cloud.discoveryengine.v1.DocumentProcessingConfig.ChunkingConfig type DocumentProcessingConfig_ChunkingConfig struct { // Configuration for the layout based chunking. + // +kcc:proto=google.cloud.discoveryengine.v1.DocumentProcessingConfig.ChunkingConfig.layout_based_chunking_config LayoutBasedChunkingConfig *DocumentProcessingConfig_ChunkingConfig_LayoutBasedChunkingConfig `json:"layoutBasedChunkingConfig,omitempty"` } @@ -67,25 +77,30 @@ type DocumentProcessingConfig_ChunkingConfig_LayoutBasedChunkingConfig struct { // // Supported values: 100-500 (inclusive). // Default value: 500. + // +kcc:proto=google.cloud.discoveryengine.v1.DocumentProcessingConfig.ChunkingConfig.LayoutBasedChunkingConfig.chunk_size ChunkSize *int32 `json:"chunkSize,omitempty"` // Whether to include appending different levels of headings to chunks // from the middle of the document to prevent context loss. // // Default value: False. + // +kcc:proto=google.cloud.discoveryengine.v1.DocumentProcessingConfig.ChunkingConfig.LayoutBasedChunkingConfig.include_ancestor_headings IncludeAncestorHeadings *bool `json:"includeAncestorHeadings,omitempty"` } // +kcc:proto=google.cloud.discoveryengine.v1.DocumentProcessingConfig.ParsingConfig type DocumentProcessingConfig_ParsingConfig struct { // Configurations applied to digital parser. + // +kcc:proto=google.cloud.discoveryengine.v1.DocumentProcessingConfig.ParsingConfig.digital_parsing_config DigitalParsingConfig *DocumentProcessingConfig_ParsingConfig_DigitalParsingConfig `json:"digitalParsingConfig,omitempty"` // Configurations applied to OCR parser. Currently it only applies to // PDFs. + // +kcc:proto=google.cloud.discoveryengine.v1.DocumentProcessingConfig.ParsingConfig.ocr_parsing_config OcrParsingConfig *DocumentProcessingConfig_ParsingConfig_OcrParsingConfig `json:"ocrParsingConfig,omitempty"` // Configurations applied to layout parser. + // +kcc:proto=google.cloud.discoveryengine.v1.DocumentProcessingConfig.ParsingConfig.layout_parsing_config LayoutParsingConfig *DocumentProcessingConfig_ParsingConfig_LayoutParsingConfig `json:"layoutParsingConfig,omitempty"` } @@ -101,10 +116,12 @@ type DocumentProcessingConfig_ParsingConfig_LayoutParsingConfig struct { type DocumentProcessingConfig_ParsingConfig_OcrParsingConfig struct { // [DEPRECATED] This field is deprecated. To use the additional enhanced // document elements processing, please switch to `layout_parsing_config`. + // +kcc:proto=google.cloud.discoveryengine.v1.DocumentProcessingConfig.ParsingConfig.OcrParsingConfig.enhanced_document_elements EnhancedDocumentElements []string `json:"enhancedDocumentElements,omitempty"` // If true, will use native text instead of OCR text on pages containing // native text. + // +kcc:proto=google.cloud.discoveryengine.v1.DocumentProcessingConfig.ParsingConfig.OcrParsingConfig.use_native_text UseNativeText *bool `json:"useNativeText,omitempty"` } @@ -119,6 +136,7 @@ type Engine_ChatEngineConfig struct { // or // [EngineService.ListEngines][google.cloud.discoveryengine.v1.EngineService.ListEngines] // API after engine creation. + // +kcc:proto=google.cloud.discoveryengine.v1.Engine.ChatEngineConfig.agent_creation_config AgentCreationConfig *Engine_ChatEngineConfig_AgentCreationConfig `json:"agentCreationConfig,omitempty"` // The resource name of an exist Dialogflow agent to link to this Chat @@ -136,6 +154,7 @@ type Engine_ChatEngineConfig struct { // API after engine creation. Use // [ChatEngineMetadata.dialogflow_agent][google.cloud.discoveryengine.v1.Engine.ChatEngineMetadata.dialogflow_agent] // for actual agent association after Engine is created. + // +kcc:proto=google.cloud.discoveryengine.v1.Engine.ChatEngineConfig.dialogflow_agent_to_link DialogflowAgentToLink *string `json:"dialogflowAgentToLink,omitempty"` } @@ -144,22 +163,26 @@ type Engine_ChatEngineConfig_AgentCreationConfig struct { // Name of the company, organization or other entity that the agent // represents. Used for knowledge connector LLM prompt and for knowledge // search. + // +kcc:proto=google.cloud.discoveryengine.v1.Engine.ChatEngineConfig.AgentCreationConfig.business Business *string `json:"business,omitempty"` // Required. The default language of the agent as a language tag. // See [Language // Support](https://cloud.google.com/dialogflow/docs/reference/language) // for a list of the currently supported language codes. + // +kcc:proto=google.cloud.discoveryengine.v1.Engine.ChatEngineConfig.AgentCreationConfig.default_language_code DefaultLanguageCode *string `json:"defaultLanguageCode,omitempty"` // Required. The time zone of the agent from the [time zone // database](https://www.iana.org/time-zones), e.g., America/New_York, // Europe/Paris. + // +kcc:proto=google.cloud.discoveryengine.v1.Engine.ChatEngineConfig.AgentCreationConfig.time_zone TimeZone *string `json:"timeZone,omitempty"` // Agent location for Agent creation, supported values: global/us/eu. // If not provided, us Engine will create Agent using us-central-1 by // default; eu Engine will create Agent using eu-west-1 by default. + // +kcc:proto=google.cloud.discoveryengine.v1.Engine.ChatEngineConfig.AgentCreationConfig.location Location *string `json:"location,omitempty"` } @@ -170,6 +193,7 @@ type Engine_ChatEngineMetadata struct { // // Format: `projects//locations//agents/`. + // +kcc:proto=google.cloud.discoveryengine.v1.Engine.ChatEngineMetadata.dialogflow_agent DialogflowAgent *string `json:"dialogflowAgent,omitempty"` } @@ -177,6 +201,7 @@ type Engine_ChatEngineMetadata struct { type Engine_CommonConfig struct { // The name of the company, business or entity that is associated with the // engine. Setting this may help improve LLM related features. + // +kcc:proto=google.cloud.discoveryengine.v1.Engine.CommonConfig.company_name CompanyName *string `json:"companyName,omitempty"` } @@ -190,18 +215,22 @@ type Engine_SearchEngineConfig struct { // Defaults to // [SearchTier.SEARCH_TIER_STANDARD][google.cloud.discoveryengine.v1.SearchTier.SEARCH_TIER_STANDARD] // if not specified. + // +kcc:proto=google.cloud.discoveryengine.v1.Engine.SearchEngineConfig.search_tier SearchTier *string `json:"searchTier,omitempty"` // The add-on that this search engine enables. + // +kcc:proto=google.cloud.discoveryengine.v1.Engine.SearchEngineConfig.search_add_ons SearchAddOns []string `json:"searchAddOns,omitempty"` } // +kcc:proto=google.cloud.discoveryengine.v1.Schema type Schema struct { // The structured representation of the schema. + // +kcc:proto=google.cloud.discoveryengine.v1.Schema.struct_schema StructSchema map[string]string `json:"structSchema,omitempty"` // The JSON representation of the schema. + // +kcc:proto=google.cloud.discoveryengine.v1.Schema.json_schema JsonSchema *string `json:"jsonSchema,omitempty"` // Immutable. The full resource name of the schema, in the format of @@ -209,24 +238,55 @@ type Schema struct { // // This field must be a UTF-8 encoded string with a length limit of 1024 // characters. + // +kcc:proto=google.cloud.discoveryengine.v1.Schema.name Name *string `json:"name,omitempty"` } +// +kcc:proto=google.cloud.discoveryengine.v1.SiteVerificationInfo +type SiteVerificationInfo struct { + // Site verification state indicating the ownership and validity. + // +kcc:proto=google.cloud.discoveryengine.v1.SiteVerificationInfo.site_verification_state + SiteVerificationState *string `json:"siteVerificationState,omitempty"` + + // Latest site verification time. + // +kcc:proto=google.cloud.discoveryengine.v1.SiteVerificationInfo.verify_time + VerifyTime *string `json:"verifyTime,omitempty"` +} + +// +kcc:proto=google.cloud.discoveryengine.v1.TargetSite.FailureReason +type TargetSite_FailureReason struct { + // Failed due to insufficient quota. + // +kcc:proto=google.cloud.discoveryengine.v1.TargetSite.FailureReason.quota_failure + QuotaFailure *TargetSite_FailureReason_QuotaFailure `json:"quotaFailure,omitempty"` +} + +// +kcc:proto=google.cloud.discoveryengine.v1.TargetSite.FailureReason.QuotaFailure +type TargetSite_FailureReason_QuotaFailure struct { + // This number is an estimation on how much total quota this project needs + // to successfully complete indexing. + // +kcc:proto=google.cloud.discoveryengine.v1.TargetSite.FailureReason.QuotaFailure.total_required_quota + TotalRequiredQuota *int64 `json:"totalRequiredQuota,omitempty"` +} + // +kcc:proto=google.cloud.discoveryengine.v1.WorkspaceConfig type WorkspaceConfig struct { // The Google Workspace data source. + // +kcc:proto=google.cloud.discoveryengine.v1.WorkspaceConfig.type Type *string `json:"type,omitempty"` // Obfuscated Dasher customer ID. + // +kcc:proto=google.cloud.discoveryengine.v1.WorkspaceConfig.dasher_customer_id DasherCustomerID *string `json:"dasherCustomerID,omitempty"` // Optional. The super admin service account for the workspace that will be // used for access token generation. For now we only use it for Native Google // Drive connector data ingestion. + // +kcc:proto=google.cloud.discoveryengine.v1.WorkspaceConfig.super_admin_service_account SuperAdminServiceAccount *string `json:"superAdminServiceAccount,omitempty"` // Optional. The super admin email address for the workspace that will be used // for access token generation. For now we only use it for Native Google Drive // connector data ingestion. + // +kcc:proto=google.cloud.discoveryengine.v1.WorkspaceConfig.super_admin_email_address SuperAdminEmailAddress *string `json:"superAdminEmailAddress,omitempty"` } diff --git a/apis/discoveryengine/v1alpha1/zz_generated.deepcopy.go b/apis/discoveryengine/v1alpha1/zz_generated.deepcopy.go index 7bd52d6577..2ecf3d4ebe 100644 --- a/apis/discoveryengine/v1alpha1/zz_generated.deepcopy.go +++ b/apis/discoveryengine/v1alpha1/zz_generated.deepcopy.go @@ -298,6 +298,180 @@ func (in *DiscoveryEngineDataStoreStatus) DeepCopy() *DiscoveryEngineDataStoreSt return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DiscoveryEngineDataStoreTargetSite) DeepCopyInto(out *DiscoveryEngineDataStoreTargetSite) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DiscoveryEngineDataStoreTargetSite. +func (in *DiscoveryEngineDataStoreTargetSite) DeepCopy() *DiscoveryEngineDataStoreTargetSite { + if in == nil { + return nil + } + out := new(DiscoveryEngineDataStoreTargetSite) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DiscoveryEngineDataStoreTargetSite) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DiscoveryEngineDataStoreTargetSiteList) DeepCopyInto(out *DiscoveryEngineDataStoreTargetSiteList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]DiscoveryEngineDataStoreTargetSite, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DiscoveryEngineDataStoreTargetSiteList. +func (in *DiscoveryEngineDataStoreTargetSiteList) DeepCopy() *DiscoveryEngineDataStoreTargetSiteList { + if in == nil { + return nil + } + out := new(DiscoveryEngineDataStoreTargetSiteList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DiscoveryEngineDataStoreTargetSiteList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DiscoveryEngineDataStoreTargetSiteObservedState) DeepCopyInto(out *DiscoveryEngineDataStoreTargetSiteObservedState) { + *out = *in + if in.GeneratedUriPattern != nil { + in, out := &in.GeneratedUriPattern, &out.GeneratedUriPattern + *out = new(string) + **out = **in + } + if in.RootDomainUri != nil { + in, out := &in.RootDomainUri, &out.RootDomainUri + *out = new(string) + **out = **in + } + if in.SiteVerificationInfo != nil { + in, out := &in.SiteVerificationInfo, &out.SiteVerificationInfo + *out = new(SiteVerificationInfo) + (*in).DeepCopyInto(*out) + } + if in.IndexingStatus != nil { + in, out := &in.IndexingStatus, &out.IndexingStatus + *out = new(string) + **out = **in + } + if in.UpdateTime != nil { + in, out := &in.UpdateTime, &out.UpdateTime + *out = new(string) + **out = **in + } + if in.FailureReason != nil { + in, out := &in.FailureReason, &out.FailureReason + *out = new(TargetSite_FailureReason) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DiscoveryEngineDataStoreTargetSiteObservedState. +func (in *DiscoveryEngineDataStoreTargetSiteObservedState) DeepCopy() *DiscoveryEngineDataStoreTargetSiteObservedState { + if in == nil { + return nil + } + out := new(DiscoveryEngineDataStoreTargetSiteObservedState) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DiscoveryEngineDataStoreTargetSiteSpec) DeepCopyInto(out *DiscoveryEngineDataStoreTargetSiteSpec) { + *out = *in + if in.DataStoreRef != nil { + in, out := &in.DataStoreRef, &out.DataStoreRef + *out = new(DiscoveryEngineDataStoreRef) + **out = **in + } + if in.ProvidedUriPattern != nil { + in, out := &in.ProvidedUriPattern, &out.ProvidedUriPattern + *out = new(string) + **out = **in + } + if in.Type != nil { + in, out := &in.Type, &out.Type + *out = new(string) + **out = **in + } + if in.ExactMatch != nil { + in, out := &in.ExactMatch, &out.ExactMatch + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DiscoveryEngineDataStoreTargetSiteSpec. +func (in *DiscoveryEngineDataStoreTargetSiteSpec) DeepCopy() *DiscoveryEngineDataStoreTargetSiteSpec { + if in == nil { + return nil + } + out := new(DiscoveryEngineDataStoreTargetSiteSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DiscoveryEngineDataStoreTargetSiteStatus) DeepCopyInto(out *DiscoveryEngineDataStoreTargetSiteStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]k8sv1alpha1.Condition, len(*in)) + copy(*out, *in) + } + if in.ObservedGeneration != nil { + in, out := &in.ObservedGeneration, &out.ObservedGeneration + *out = new(int64) + **out = **in + } + if in.ExternalRef != nil { + in, out := &in.ExternalRef, &out.ExternalRef + *out = new(string) + **out = **in + } + if in.ObservedState != nil { + in, out := &in.ObservedState, &out.ObservedState + *out = new(DiscoveryEngineDataStoreTargetSiteObservedState) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DiscoveryEngineDataStoreTargetSiteStatus. +func (in *DiscoveryEngineDataStoreTargetSiteStatus) DeepCopy() *DiscoveryEngineDataStoreTargetSiteStatus { + if in == nil { + return nil + } + out := new(DiscoveryEngineDataStoreTargetSiteStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DiscoveryEngineEngine) DeepCopyInto(out *DiscoveryEngineEngine) { *out = *in @@ -845,6 +1019,86 @@ func (in *Schema) DeepCopy() *Schema { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SiteVerificationInfo) DeepCopyInto(out *SiteVerificationInfo) { + *out = *in + if in.SiteVerificationState != nil { + in, out := &in.SiteVerificationState, &out.SiteVerificationState + *out = new(string) + **out = **in + } + if in.VerifyTime != nil { + in, out := &in.VerifyTime, &out.VerifyTime + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SiteVerificationInfo. +func (in *SiteVerificationInfo) DeepCopy() *SiteVerificationInfo { + if in == nil { + return nil + } + out := new(SiteVerificationInfo) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TargetSiteRef) DeepCopyInto(out *TargetSiteRef) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TargetSiteRef. +func (in *TargetSiteRef) DeepCopy() *TargetSiteRef { + if in == nil { + return nil + } + out := new(TargetSiteRef) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TargetSite_FailureReason) DeepCopyInto(out *TargetSite_FailureReason) { + *out = *in + if in.QuotaFailure != nil { + in, out := &in.QuotaFailure, &out.QuotaFailure + *out = new(TargetSite_FailureReason_QuotaFailure) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TargetSite_FailureReason. +func (in *TargetSite_FailureReason) DeepCopy() *TargetSite_FailureReason { + if in == nil { + return nil + } + out := new(TargetSite_FailureReason) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TargetSite_FailureReason_QuotaFailure) DeepCopyInto(out *TargetSite_FailureReason_QuotaFailure) { + *out = *in + if in.TotalRequiredQuota != nil { + in, out := &in.TotalRequiredQuota, &out.TotalRequiredQuota + *out = new(int64) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TargetSite_FailureReason_QuotaFailure. +func (in *TargetSite_FailureReason_QuotaFailure) DeepCopy() *TargetSite_FailureReason_QuotaFailure { + if in == nil { + return nil + } + out := new(TargetSite_FailureReason_QuotaFailure) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WorkspaceConfig) DeepCopyInto(out *WorkspaceConfig) { *out = *in diff --git a/apis/refs/v1beta1/computerefs.go b/apis/refs/v1beta1/computerefs.go index 707d95397d..86fdcbcc6a 100644 --- a/apis/refs/v1beta1/computerefs.go +++ b/apis/refs/v1beta1/computerefs.go @@ -20,8 +20,7 @@ import ( "strconv" "strings" - resourcemanager "cloud.google.com/go/resourcemanager/apiv3" - resourcemanagerpb "cloud.google.com/go/resourcemanager/apiv3/resourcemanagerpb" + "github.com/GoogleCloudPlatform/k8s-config-connector/apis/common/projects" "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/k8s" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -74,7 +73,7 @@ func ParseComputeNetworkID(external string) (*ComputeNetworkID, error) { } // ConvertToProjectNumber converts the external reference to use a project number. -func (ref *ComputeNetworkRef) ConvertToProjectNumber(ctx context.Context, projectsClient *resourcemanager.ProjectsClient) error { +func (ref *ComputeNetworkRef) ConvertToProjectNumber(ctx context.Context, projectMapper *projects.ProjectMapper) error { if ref == nil { return nil } @@ -84,24 +83,12 @@ func (ref *ComputeNetworkRef) ConvertToProjectNumber(ctx context.Context, projec return err } - // Check if the project number is already a valid integer - // If not, we need to look it up - projectNumber, err := strconv.ParseInt(id.Project, 10, 64) + projectNumber, err := projectMapper.LookupProjectNumber(ctx, id.Project) if err != nil { - req := &resourcemanagerpb.GetProjectRequest{ - Name: "projects/" + id.Project, - } - project, err := projectsClient.GetProject(ctx, req) - if err != nil { - return fmt.Errorf("error getting project %q: %w", req.Name, err) - } - n, err := strconv.ParseInt(strings.TrimPrefix(project.Name, "projects/"), 10, 64) - if err != nil { - return fmt.Errorf("error parsing project number for %q: %w", project.Name, err) - } - projectNumber = n + return fmt.Errorf("error looking up project number for project %q: %w", id.Project, err) } id.Project = strconv.FormatInt(projectNumber, 10) + ref.External = id.String() return nil } diff --git a/config/crds/resources/apiextensions.k8s.io_v1_customresourcedefinition_discoveryenginedatastoretargetsites.discoveryengine.cnrm.cloud.google.com.yaml b/config/crds/resources/apiextensions.k8s.io_v1_customresourcedefinition_discoveryenginedatastoretargetsites.discoveryengine.cnrm.cloud.google.com.yaml new file mode 100644 index 0000000000..fd88ad7aa9 --- /dev/null +++ b/config/crds/resources/apiextensions.k8s.io_v1_customresourcedefinition_discoveryenginedatastoretargetsites.discoveryengine.cnrm.cloud.google.com.yaml @@ -0,0 +1,203 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cnrm.cloud.google.com/version: 0.0.0-dev + creationTimestamp: null + labels: + cnrm.cloud.google.com/managed-by-kcc: "true" + cnrm.cloud.google.com/system: "true" + name: discoveryenginedatastoretargetsites.discoveryengine.cnrm.cloud.google.com +spec: + group: discoveryengine.cnrm.cloud.google.com + names: + categories: + - gcp + kind: DiscoveryEngineDataStoreTargetSite + listKind: DiscoveryEngineDataStoreTargetSiteList + plural: discoveryenginedatastoretargetsites + shortNames: + - gcpdiscoveryenginedatastoretargetsite + - gcpdiscoveryenginedatastoretargetsites + singular: discoveryenginedatastoretargetsite + preserveUnknownFields: false + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - description: When 'True', the most recent reconcile of the resource succeeded + jsonPath: .status.conditions[?(@.type=='Ready')].status + name: Ready + type: string + - description: The reason for the value in 'Ready' + jsonPath: .status.conditions[?(@.type=='Ready')].reason + name: Status + type: string + - description: The last transition time for the value in 'Status' + jsonPath: .status.conditions[?(@.type=='Ready')].lastTransitionTime + name: Status Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: DiscoveryEngineDataStoreTargetSite is the Schema for the DiscoveryEngineDataStoreTargetSite + API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: DiscoveryEngineDataStoreTargetSiteSpec defines the desired + state of DiscoveryEngineDataStoreTargetSite + properties: + dataStoreRef: + description: The DataStore this target site should be part of. + oneOf: + - not: + required: + - external + required: + - name + - not: + anyOf: + - required: + - name + - required: + - namespace + required: + - external + properties: + external: + description: A reference to an externally managed DiscoveryEngineDataStore + resource. Should be in the format "projects/{{projectID}}/locations/{{location}}/datastores/{{datastoreID}}". + type: string + name: + description: The name of a DiscoveryEngineDataStore resource. + type: string + namespace: + description: The namespace of a DiscoveryEngineDataStore resource. + type: string + type: object + exactMatch: + description: Input only. If set to false, a uri_pattern is generated + to include all pages whose address contains the provided_uri_pattern. + If set to true, an uri_pattern is generated to try to be an exact + match of the provided_uri_pattern or just the specific page if the + provided_uri_pattern is a specific one. provided_uri_pattern is + always normalized to generate the URI pattern to be used by the + search engine. + type: boolean + providedUriPattern: + description: Required. Input only. The user provided URI pattern from + which the `generated_uri_pattern` is generated. + type: string + type: + description: The type of the target site, e.g., whether the site is + to be included or excluded. + type: string + type: object + status: + description: DiscoveryEngineDataStoreTargetSiteStatus defines the config + connector machine state of DiscoveryEngineDataStoreTargetSite + properties: + conditions: + description: Conditions represent the latest available observations + of the object's current state. + items: + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. + type: string + message: + description: Human-readable message indicating details about + last transition. + type: string + reason: + description: Unique, one-word, CamelCase reason for the condition's + last transition. + type: string + status: + description: Status is the status of the condition. Can be True, + False, Unknown. + type: string + type: + description: Type is the type of the condition. + type: string + type: object + type: array + externalRef: + description: A unique specifier for the DiscoveryEngineDataStoreTargetSite + resource in GCP. + type: string + observedGeneration: + description: 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. + format: int64 + type: integer + observedState: + description: ObservedState is the state of the resource as most recently + observed in GCP. + properties: + failureReason: + description: Output only. Failure reason. + properties: + quotaFailure: + description: Failed due to insufficient quota. + properties: + totalRequiredQuota: + description: This number is an estimation on how much + total quota this project needs to successfully complete + indexing. + format: int64 + type: integer + type: object + type: object + generatedUriPattern: + description: Output only. This is system-generated based on the + provided_uri. + type: string + indexingStatus: + description: Output only. Indexing status. + type: string + rootDomainUri: + description: Output only. Root domain of the provided_uri. + type: string + siteVerificationInfo: + description: Output only. Site ownership and validity verification + status. + properties: + siteVerificationState: + description: Site verification state indicating the ownership + and validity. + type: string + verifyTime: + description: Latest site verification time. + type: string + type: object + updateTime: + description: Output only. The target site's last updated time. + type: string + type: object + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/tests/samples/create/harness.go b/config/tests/samples/create/harness.go index 9a999ec696..73e256142b 100644 --- a/config/tests/samples/create/harness.go +++ b/config/tests/samples/create/harness.go @@ -793,6 +793,7 @@ func MaybeSkip(t *testing.T, name string, resources []*unstructured.Unstructured case schema.GroupKind{Group: "dataform.cnrm.cloud.google.com", Kind: "DataformRepository"}: case schema.GroupKind{Group: "discoveryengine.cnrm.cloud.google.com", Kind: "DiscoveryEngineDataStore"}: + case schema.GroupKind{Group: "discoveryengine.cnrm.cloud.google.com", Kind: "DiscoveryEngineDataStoreTargetSite"}: case schema.GroupKind{Group: "iam.cnrm.cloud.google.com", Kind: "IAMPartialPolicy"}: case schema.GroupKind{Group: "iam.cnrm.cloud.google.com", Kind: "IAMPolicy"}: diff --git a/dev/tools/controllerbuilder/generate.sh b/dev/tools/controllerbuilder/generate.sh index af084ea513..a97263f3dd 100755 --- a/dev/tools/controllerbuilder/generate.sh +++ b/dev/tools/controllerbuilder/generate.sh @@ -29,6 +29,7 @@ go run . generate-types \ --service google.cloud.discoveryengine.v1 \ --api-version discoveryengine.cnrm.cloud.google.com/v1alpha1 \ --resource DiscoveryEngineDataStore:DataStore \ + --resource DiscoveryEngineDataStoreTargetSite:TargetSite \ --resource DiscoveryEngineEngine:Engine # go run . prompt --src-dir ~/kcc/k8s-config-connector --proto-dir ~/kcc/k8s-config-connector/dev/tools/proto-to-mapper/third_party/googleapis/ <