Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 3 additions & 2 deletions README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,9 @@ KHIは、Google Cloud サポートチームが開発し、その後オープン
- **必須権限**
- `logging.logEntries.list`
- **推奨権限**
- 対象のクラスタのタイプに対するリスト権限(例:GKE の場合 `container.clusters.list`)
ログフィルタ生成ダイアログの候補の出力に使用します。KHI の主機能の利用に影響はありません。
- New Inspectionダイアログでの入力時にオートコンプリートの入力候補を取得するために使用します。権限がなくても問題がありませんが、入力時にクラスタ名の候補が表示されません。
- `monitoring.timeSeries.list`
- `container.clusters.list` (Cloud Composer向け機能利用時のみ)
- **設定手順**

- Compute Engine 仮想マシン上など、サービスアカウントがアタッチされた Google Cloud 環境で KHI を実行する場合、対応するリソースにアタッチされたサービスアカウントに上記権限を付与します。
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,9 @@ The following permissions are required or recommended.
- **Required**
- `logging.logEntries.list`
- **Recommended**
- Permissions to list clusters for cluster type (eg. `container.clusters.list` for GKE)
This permission is used to show autofill candidates for the log filter. KHI's main functionality is not affected without this permission.
- 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.
- `monitoring.timeSeries.list`
- `container.clusters.list` (Only when using Cloud Composer features)
- **Setting**
- 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.
- Running KHI locally or on Cloud Shell with a user account: Apply the permissions above to your user account.
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ require (
cloud.google.com/go/compute/metadata v0.9.0 // indirect
cloud.google.com/go/iam v1.5.3 // indirect
cloud.google.com/go/longrunning v0.7.0 // indirect
cloud.google.com/go/monitoring v1.24.3 // indirect
cloud.google.com/go/trace v1.11.7 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.54.0 // indirect
github.com/bmatcuk/doublestar/v4 v4.0.2 // indirect
Expand Down
56 changes: 10 additions & 46 deletions pkg/api/googlecloud/clientfactory.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,9 @@ import (

compute "cloud.google.com/go/compute/apiv1"
container "cloud.google.com/go/container/apiv1"
gkehub "cloud.google.com/go/gkehub/apiv1beta1"
gkemulticloud "cloud.google.com/go/gkemulticloud/apiv1"
logging "cloud.google.com/go/logging/apiv2"
monitoring "cloud.google.com/go/monitoring/apiv3/v2"
"google.golang.org/api/composer/v1"
"google.golang.org/api/gkeonprem/v1"
"google.golang.org/api/option"
)

Expand All @@ -45,14 +43,11 @@ type ClientFactory struct {
ClientOptions []ClientFactoryOptionsModifiers
ContextModifiers []ClientFactoryContextModifiers

ContainerClusterManagerClientOptions []ClientFactoryOptionsModifiers
GKEHubMembershipClientOptions []ClientFactoryOptionsModifiers
GKEMultiCloudAWSClustersClientOptions []ClientFactoryOptionsModifiers
GKEMultiCloudAzureClustersClientOptions []ClientFactoryOptionsModifiers
LoggingClientOptions []ClientFactoryOptionsModifiers
RegionsClientOptions []ClientFactoryOptionsModifiers
ComposerServiceOptions []ClientFactoryOptionsModifiers
GKEOnPremServiceOptions []ClientFactoryOptionsModifiers
ContainerClusterManagerClientOptions []ClientFactoryOptionsModifiers
LoggingClientOptions []ClientFactoryOptionsModifiers
RegionsClientOptions []ClientFactoryOptionsModifiers
ComposerServiceOptions []ClientFactoryOptionsModifiers
MonitoringMetricClientOptions []ClientFactoryOptionsModifiers
}

// NewClientFactory creates a new ClientFactory with the given options.
Expand Down Expand Up @@ -118,36 +113,6 @@ func (s *ClientFactory) ContainerClusterManagerClient(ctx context.Context, c Res
return container.NewClusterManagerClient(ctx, opts...)
}

// GKEHubMembershipClient returns the MembershipClient of gkehub.googleapis.com from given context and the resource container.
func (s *ClientFactory) GKEHubMembershipClient(ctx context.Context, c ResourceContainer, opts ...option.ClientOption) (*gkehub.GkeHubMembershipClient, error) {
ctx, opts, err := s.prepareServiceInput(ctx, c, s.GKEHubMembershipClientOptions, opts...)
if err != nil {
return nil, err
}

return gkehub.NewGkeHubMembershipClient(ctx, opts...)
}

// GKEMultiCloudAWSClustersClient returns the AwsClusterClient of gkemulticloud.googleapis.com from given context and the resource container.
func (s *ClientFactory) GKEMultiCloudAWSClustersClient(ctx context.Context, c ResourceContainer, opts ...option.ClientOption) (*gkemulticloud.AwsClustersClient, error) {
ctx, opts, err := s.prepareServiceInput(ctx, c, s.GKEMultiCloudAWSClustersClientOptions, opts...)
if err != nil {
return nil, err
}

return gkemulticloud.NewAwsClustersClient(ctx, opts...)
}

// GKEMultiCloudAzureClustersClient returns the AzureClustersClient of gkemulticloud.googleapis.com from given context and the resource container.
func (s *ClientFactory) GKEMultiCloudAzureClustersClient(ctx context.Context, c ResourceContainer, opts ...option.ClientOption) (*gkemulticloud.AzureClustersClient, error) {
ctx, opts, err := s.prepareServiceInput(ctx, c, s.GKEMultiCloudAzureClustersClientOptions, opts...)
if err != nil {
return nil, err
}

return gkemulticloud.NewAzureClustersClient(ctx, opts...)
}

// LoggingClient returns the client for logging.googleapis.com from given context and the resource container.
func (s *ClientFactory) LoggingClient(ctx context.Context, c ResourceContainer, opts ...option.ClientOption) (*logging.Client, error) {
ctx, opts, err := s.prepareServiceInput(ctx, c, s.LoggingClientOptions, opts...)
Expand Down Expand Up @@ -178,13 +143,12 @@ func (s *ClientFactory) ComposerService(ctx context.Context, c ResourceContainer
return composer.NewService(ctx, opts...)
}

// GKEOnPremService returns the client for gkeonprem.googleapis.com from the given context and the resource cntainer.
// 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'.
func (s *ClientFactory) GKEOnPremService(ctx context.Context, c ResourceContainer, opts ...option.ClientOption) (*gkeonprem.Service, error) {
ctx, opts, err := s.prepareServiceInput(ctx, c, s.GKEOnPremServiceOptions, opts...)
// MonitoringMetricClient returns the client for monitoring.googleapis.com from given context and the resource container.
func (s *ClientFactory) MonitoringMetricClient(ctx context.Context, c ResourceContainer, opts ...option.ClientOption) (*monitoring.MetricClient, error) {
ctx, opts, err := s.prepareServiceInput(ctx, c, s.MonitoringMetricClientOptions, opts...)
if err != nil {
return nil, err
}

return gkeonprem.NewService(ctx, opts...)
return monitoring.NewMetricClient(ctx, opts...)
}
81 changes: 81 additions & 0 deletions pkg/api/googlecloud/monitoring_util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// 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.

package googlecloud

import (
"context"
"fmt"
"time"

monitoring "cloud.google.com/go/monitoring/apiv3/v2"
"cloud.google.com/go/monitoring/apiv3/v2/monitoringpb"
"google.golang.org/api/iterator"
"google.golang.org/protobuf/types/known/durationpb"
"google.golang.org/protobuf/types/known/timestamppb"
)

// QueryDistinctLabelValuesFromMetrics queries Cloud Monitoring for TimeSeries matching the filter and interval,
// and returns unique values for the specified label key.
//
// groupByKey: The full label key to group by (e.g. "resource.label.cluster_name").
// resultLabelKey: The simple label key to extract from the result (e.g. "cluster_name").
func QueryDistinctLabelValuesFromMetrics(ctx context.Context, client *monitoring.MetricClient, projectID string, filter string, startTime, endTime time.Time, groupByKey, resultLabelKey string) ([]string, error) {
d := endTime.Sub(startTime)
if d < 60*time.Second {
d = 60 * time.Second
}
req := &monitoringpb.ListTimeSeriesRequest{
Name: "projects/" + projectID,
Filter: filter,
Interval: &monitoringpb.TimeInterval{
StartTime: timestamppb.New(startTime),
EndTime: timestamppb.New(endTime),
},
View: monitoringpb.ListTimeSeriesRequest_HEADERS,
Aggregation: &monitoringpb.Aggregation{
AlignmentPeriod: &durationpb.Duration{Seconds: int64(d.Seconds())},
PerSeriesAligner: monitoringpb.Aggregation_ALIGN_SUM,
CrossSeriesReducer: monitoringpb.Aggregation_REDUCE_NONE,
GroupByFields: []string{groupByKey},
},
}

it := client.ListTimeSeries(ctx, req)
uniqueValues := make(map[string]struct{})
for {
resp, err := it.Next()
if err == iterator.Done {
break
}
if err != nil {
return nil, fmt.Errorf("failed to list time series: %w", err)
}

// Attempt to find the label in Resource or Metric labels
val, ok := resp.GetResource().GetLabels()[resultLabelKey]
if !ok {
val, ok = resp.GetMetric().GetLabels()[resultLabelKey]
}
if ok {
uniqueValues[val] = struct{}{}
}
}

result := make([]string, 0, len(uniqueValues))
for v := range uniqueValues {
result = append(result, v)
}
return result, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (
var GoogleCloudComposerTaskIDPrefix = "cloud.google.com/composer/"

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

// ComposerClusterNamePrefixTaskID is the task id for the task that returns the GKE cluster name prefix used by Cloud Composer.
var ComposerClusterNamePrefixTaskID = taskid.NewImplementationID(googlecloudk8scommon_contract.ClusterNamePrefixTaskID, "composer")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,6 @@ Note: Composer 3 does not run on your GKE. Please remove all Kubernetes/GKE ques
ClusterNames: []string{clusterName},
},
}, nil
}, inspectioncore_contract.InspectionTypeLabel(googlecloudclustercomposer_contract.InspectionTypeId))
}, inspectioncore_contract.InspectionTypeLabel(googlecloudclustercomposer_contract.InspectionTypeId),
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.
)

This file was deleted.

Loading