Skip to content

Commit d78fd2d

Browse files
committed
feat(googlecloud): use Cloud Monitoring for generic cluster autocomplete
Refactors cluster name autocomplete to utilize Cloud Monitoring, enabling historical cluster discovery. This change centralizes the autocomplete logic and removes provider-specific implementations. - feat: implement generic `AutocompleteClusterNamesTask` in `googlecloudk8scommon` using Cloud Monitoring metrics. - feat: add `QueryDistinctLabelValuesFromMetrics` utility for generic metric label querying. - refactor: remove specific autocomplete tasks for GKE, GDC, and MultiCloud in favor of the common implementation. - fix: correct task dependencies in `InputClusterNameTask` to use reference IDs. - doc: update README.md and README.ja.md with new optional permissions for autocomplete.
1 parent beaec97 commit d78fd2d

46 files changed

Lines changed: 260 additions & 2152 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.ja.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,9 @@ KHIは、Google Cloud サポートチームが開発し、その後オープン
159159
- **必須権限**
160160
- `logging.logEntries.list`
161161
- **推奨権限**
162-
- 対象のクラスタのタイプに対するリスト権限(例:GKE の場合 `container.clusters.list`
163-
ログフィルタ生成ダイアログの候補の出力に使用します。KHI の主機能の利用に影響はありません。
162+
- New Inspectionダイアログでの入力時にオートコンプリートの入力候補を取得するために使用します。権限がなくても問題がありませんが、入力時にクラスタ名の候補が表示されません。
163+
- `monitoring.timeSeries.list`
164+
- `container.clusters.list` (Cloud Composer向け機能利用時のみ)
164165
- **設定手順**
165166
166167
- Compute Engine 仮想マシン上など、サービスアカウントがアタッチされた Google Cloud 環境で KHI を実行する場合、対応するリソースにアタッチされたサービスアカウントに上記権限を付与します。

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,9 @@ The following permissions are required or recommended.
151151
- **Required**
152152
- `logging.logEntries.list`
153153
- **Recommended**
154-
- Permissions to list clusters for cluster type (eg. `container.clusters.list` for GKE)
155-
This permission is used to show autofill candidates for the log filter. KHI's main functionality is not affected without this permission.
154+
- These permissions are used to fetch autocomplete candidates in the New Inspection dialog. KHI works without these permissions, but cluster name suggestions will not be displayed.
155+
- `monitoring.timeSeries.list`
156+
- `container.clusters.list` (Only when using Cloud Composer features)
156157
- **Setting**
157158
- Running KHI on environments with a service account attached, such as Google Cloud Compute Engine Instance: Apply the permissions above to the attached service account.
158159
- Running KHI locally or on Cloud Shell with a user account: Apply the permissions above to your user account.

pkg/api/googlecloud/clientfactory.go

Lines changed: 5 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,9 @@ import (
1919

2020
compute "cloud.google.com/go/compute/apiv1"
2121
container "cloud.google.com/go/container/apiv1"
22-
gkehub "cloud.google.com/go/gkehub/apiv1beta1"
23-
gkemulticloud "cloud.google.com/go/gkemulticloud/apiv1"
2422
logging "cloud.google.com/go/logging/apiv2"
2523
monitoring "cloud.google.com/go/monitoring/apiv3/v2"
2624
"google.golang.org/api/composer/v1"
27-
"google.golang.org/api/gkeonprem/v1"
2825
"google.golang.org/api/option"
2926
)
3027

@@ -46,15 +43,11 @@ type ClientFactory struct {
4643
ClientOptions []ClientFactoryOptionsModifiers
4744
ContextModifiers []ClientFactoryContextModifiers
4845

49-
ContainerClusterManagerClientOptions []ClientFactoryOptionsModifiers
50-
GKEHubMembershipClientOptions []ClientFactoryOptionsModifiers
51-
GKEMultiCloudAWSClustersClientOptions []ClientFactoryOptionsModifiers
52-
GKEMultiCloudAzureClustersClientOptions []ClientFactoryOptionsModifiers
53-
LoggingClientOptions []ClientFactoryOptionsModifiers
54-
RegionsClientOptions []ClientFactoryOptionsModifiers
55-
ComposerServiceOptions []ClientFactoryOptionsModifiers
56-
GKEOnPremServiceOptions []ClientFactoryOptionsModifiers
57-
MonitoringMetricClientOptions []ClientFactoryOptionsModifiers
46+
ContainerClusterManagerClientOptions []ClientFactoryOptionsModifiers
47+
LoggingClientOptions []ClientFactoryOptionsModifiers
48+
RegionsClientOptions []ClientFactoryOptionsModifiers
49+
ComposerServiceOptions []ClientFactoryOptionsModifiers
50+
MonitoringMetricClientOptions []ClientFactoryOptionsModifiers
5851
}
5952

6053
// NewClientFactory creates a new ClientFactory with the given options.
@@ -120,36 +113,6 @@ func (s *ClientFactory) ContainerClusterManagerClient(ctx context.Context, c Res
120113
return container.NewClusterManagerClient(ctx, opts...)
121114
}
122115

123-
// GKEHubMembershipClient returns the MembershipClient of gkehub.googleapis.com from given context and the resource container.
124-
func (s *ClientFactory) GKEHubMembershipClient(ctx context.Context, c ResourceContainer, opts ...option.ClientOption) (*gkehub.GkeHubMembershipClient, error) {
125-
ctx, opts, err := s.prepareServiceInput(ctx, c, s.GKEHubMembershipClientOptions, opts...)
126-
if err != nil {
127-
return nil, err
128-
}
129-
130-
return gkehub.NewGkeHubMembershipClient(ctx, opts...)
131-
}
132-
133-
// GKEMultiCloudAWSClustersClient returns the AwsClusterClient of gkemulticloud.googleapis.com from given context and the resource container.
134-
func (s *ClientFactory) GKEMultiCloudAWSClustersClient(ctx context.Context, c ResourceContainer, opts ...option.ClientOption) (*gkemulticloud.AwsClustersClient, error) {
135-
ctx, opts, err := s.prepareServiceInput(ctx, c, s.GKEMultiCloudAWSClustersClientOptions, opts...)
136-
if err != nil {
137-
return nil, err
138-
}
139-
140-
return gkemulticloud.NewAwsClustersClient(ctx, opts...)
141-
}
142-
143-
// GKEMultiCloudAzureClustersClient returns the AzureClustersClient of gkemulticloud.googleapis.com from given context and the resource container.
144-
func (s *ClientFactory) GKEMultiCloudAzureClustersClient(ctx context.Context, c ResourceContainer, opts ...option.ClientOption) (*gkemulticloud.AzureClustersClient, error) {
145-
ctx, opts, err := s.prepareServiceInput(ctx, c, s.GKEMultiCloudAzureClustersClientOptions, opts...)
146-
if err != nil {
147-
return nil, err
148-
}
149-
150-
return gkemulticloud.NewAzureClustersClient(ctx, opts...)
151-
}
152-
153116
// LoggingClient returns the client for logging.googleapis.com from given context and the resource container.
154117
func (s *ClientFactory) LoggingClient(ctx context.Context, c ResourceContainer, opts ...option.ClientOption) (*logging.Client, error) {
155118
ctx, opts, err := s.prepareServiceInput(ctx, c, s.LoggingClientOptions, opts...)
@@ -180,17 +143,6 @@ func (s *ClientFactory) ComposerService(ctx context.Context, c ResourceContainer
180143
return composer.NewService(ctx, opts...)
181144
}
182145

183-
// GKEOnPremService returns the client for gkeonprem.googleapis.com from the given context and the resource cntainer.
184-
// GKEOnPrem has no package defined by `cloud.google.com/go`, this method returns the low level API client from '"google.golang.org/api/gkeonprem/v1'.
185-
func (s *ClientFactory) GKEOnPremService(ctx context.Context, c ResourceContainer, opts ...option.ClientOption) (*gkeonprem.Service, error) {
186-
ctx, opts, err := s.prepareServiceInput(ctx, c, s.GKEOnPremServiceOptions, opts...)
187-
if err != nil {
188-
return nil, err
189-
}
190-
191-
return gkeonprem.NewService(ctx, opts...)
192-
}
193-
194146
// MonitoringMetricClient returns the client for monitoring.googleapis.com from given context and the resource container.
195147
func (s *ClientFactory) MonitoringMetricClient(ctx context.Context, c ResourceContainer, opts ...option.ClientOption) (*monitoring.MetricClient, error) {
196148
ctx, opts, err := s.prepareServiceInput(ctx, c, s.MonitoringMetricClientOptions, opts...)
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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+
package googlecloud
16+
17+
import (
18+
"context"
19+
"fmt"
20+
"time"
21+
22+
monitoring "cloud.google.com/go/monitoring/apiv3/v2"
23+
"cloud.google.com/go/monitoring/apiv3/v2/monitoringpb"
24+
"google.golang.org/api/iterator"
25+
"google.golang.org/protobuf/types/known/durationpb"
26+
"google.golang.org/protobuf/types/known/timestamppb"
27+
)
28+
29+
// QueryDistinctLabelValuesFromMetrics queries Cloud Monitoring for TimeSeries matching the filter and interval,
30+
// and returns unique values for the specified label key.
31+
//
32+
// groupByKey: The full label key to group by (e.g. "resource.label.cluster_name").
33+
// resultLabelKey: The simple label key to extract from the result (e.g. "cluster_name").
34+
func QueryDistinctLabelValuesFromMetrics(ctx context.Context, client *monitoring.MetricClient, projectID string, filter string, startTime, endTime time.Time, groupByKey, resultLabelKey string) ([]string, error) {
35+
d := endTime.Sub(startTime)
36+
if d < 60*time.Second {
37+
d = 60 * time.Second
38+
}
39+
req := &monitoringpb.ListTimeSeriesRequest{
40+
Name: "projects/" + projectID,
41+
Filter: filter,
42+
Interval: &monitoringpb.TimeInterval{
43+
StartTime: timestamppb.New(startTime),
44+
EndTime: timestamppb.New(endTime),
45+
},
46+
View: monitoringpb.ListTimeSeriesRequest_HEADERS,
47+
Aggregation: &monitoringpb.Aggregation{
48+
AlignmentPeriod: &durationpb.Duration{Seconds: int64(d.Seconds())},
49+
PerSeriesAligner: monitoringpb.Aggregation_ALIGN_SUM,
50+
CrossSeriesReducer: monitoringpb.Aggregation_REDUCE_NONE,
51+
GroupByFields: []string{groupByKey},
52+
},
53+
}
54+
55+
it := client.ListTimeSeries(ctx, req)
56+
uniqueValues := make(map[string]struct{})
57+
for {
58+
resp, err := it.Next()
59+
if err == iterator.Done {
60+
break
61+
}
62+
if err != nil {
63+
return nil, fmt.Errorf("failed to list time series: %w", err)
64+
}
65+
66+
// Attempt to find the label in Resource or Metric labels
67+
val, ok := resp.GetResource().GetLabels()[resultLabelKey]
68+
if !ok {
69+
val, ok = resp.GetMetric().GetLabels()[resultLabelKey]
70+
}
71+
if ok {
72+
uniqueValues[val] = struct{}{}
73+
}
74+
}
75+
76+
result := make([]string, 0, len(uniqueValues))
77+
for v := range uniqueValues {
78+
result = append(result, v)
79+
}
80+
return result, nil
81+
}

pkg/task/inspection/googlecloudclustercomposer/contract/taskid.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import (
2424
var GoogleCloudComposerTaskIDPrefix = "cloud.google.com/composer/"
2525

2626
// AutocompleteComposerClusterNamesTaskID is the task id for the task that autocompletes GKE cluster names created by Cloud Composer.
27-
var AutocompleteComposerClusterNamesTaskID = taskid.NewImplementationID(googlecloudk8scommon_contract.AutocompleteClusterNamesTaskID, "composer")
27+
var AutocompleteComposerClusterNamesTaskID = taskid.NewImplementationID(googlecloudk8scommon_contract.AutocompleteClusterNamesTaskID.Ref(), "composer")
2828

2929
// ComposerClusterNamePrefixTaskID is the task id for the task that returns the GKE cluster name prefix used by Cloud Composer.
3030
var ComposerClusterNamePrefixTaskID = taskid.NewImplementationID(googlecloudk8scommon_contract.ClusterNamePrefixTaskID, "composer")

pkg/task/inspection/googlecloudclustercomposer/impl/autocompletecomposerclusternames_task.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,6 @@ Note: Composer 3 does not run on your GKE. Please remove all Kubernetes/GKE ques
8484
ClusterNames: []string{clusterName},
8585
},
8686
}, nil
87-
}, inspectioncore_contract.InspectionTypeLabel(googlecloudclustercomposer_contract.InspectionTypeId))
87+
}, inspectioncore_contract.InspectionTypeLabel(googlecloudclustercomposer_contract.InspectionTypeId),
88+
coretask.WithSelectionPriority(1000), // Setting higher priority compared to the default autocomplete cluster name finder to override it. Composer cluster finder is currently overriding the common autocomplete cluster name finder using Cloud Monitoring to compare the environment label name.
89+
)

pkg/task/inspection/googlecloudclustergdcbaremetal/contract/clusterlistfetcher.go

Lines changed: 0 additions & 141 deletions
This file was deleted.

0 commit comments

Comments
 (0)