blob: aea2a2db3298c463b5e0f91a80c7f2dc0a15d519 [file] [log] [blame]
/*
*
* Copyright 2022 gRPC authors.
*
* 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 observability
import (
"context"
"fmt"
"os"
"regexp"
gcplogging "cloud.google.com/go/logging"
"golang.org/x/oauth2/google"
configpb "google.golang.org/grpc/observability/internal/config"
"google.golang.org/protobuf/encoding/protojson"
)
const (
envObservabilityConfig = "GRPC_CONFIG_OBSERVABILITY"
envProjectID = "GOOGLE_CLOUD_PROJECT"
logFilterPatternRegexpStr = `^([\w./]+)/((?:\w+)|[*])$`
)
var logFilterPatternRegexp = regexp.MustCompile(logFilterPatternRegexpStr)
// fetchDefaultProjectID fetches the default GCP project id from environment.
func fetchDefaultProjectID(ctx context.Context) string {
// Step 1: Check ENV var
if s := os.Getenv(envProjectID); s != "" {
logger.Infof("Found project ID from env %v: %v", envProjectID, s)
return s
}
// Step 2: Check default credential
credentials, err := google.FindDefaultCredentials(ctx, gcplogging.WriteScope)
if err != nil {
logger.Infof("Failed to locate Google Default Credential: %v", err)
return ""
}
if credentials.ProjectID == "" {
logger.Infof("Failed to find project ID in default credential: %v", err)
return ""
}
logger.Infof("Found project ID from Google Default Credential: %v", credentials.ProjectID)
return credentials.ProjectID
}
func validateFilters(config *configpb.ObservabilityConfig) error {
for _, filter := range config.GetLogFilters() {
if filter.Pattern == "*" {
continue
}
match := logFilterPatternRegexp.FindStringSubmatch(filter.Pattern)
if match == nil {
return fmt.Errorf("invalid log filter pattern: %v", filter.Pattern)
}
}
return nil
}
func parseObservabilityConfig() (*configpb.ObservabilityConfig, error) {
// Parse the config from ENV var
if content := os.Getenv(envObservabilityConfig); content != "" {
var config configpb.ObservabilityConfig
if err := protojson.Unmarshal([]byte(content), &config); err != nil {
return nil, fmt.Errorf("error parsing observability config from env %v: %v", envObservabilityConfig, err)
}
if err := validateFilters(&config); err != nil {
return nil, fmt.Errorf("error parsing observability config: %v", err)
}
logger.Infof("Parsed ObservabilityConfig: %+v", &config)
return &config, nil
}
// If the ENV var doesn't exist, do nothing
return nil, nil
}
func ensureProjectIDInObservabilityConfig(ctx context.Context, config *configpb.ObservabilityConfig) error {
if config.GetDestinationProjectId() == "" {
// Try to fetch the GCP project id
projectID := fetchDefaultProjectID(ctx)
if projectID == "" {
return fmt.Errorf("empty destination project ID")
}
config.DestinationProjectId = projectID
}
return nil
}