grpc-xds/control-plane-go/pkg/config/informers.go (75 lines of code) (raw):
// Copyright 2023 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 config
import (
"errors"
"fmt"
"os"
"path/filepath"
"github.com/go-logr/logr"
"gopkg.in/yaml.v3"
"github.com/googlecloudplatform/solutions-workshops/grpc-xds/control-plane-go/pkg/informers"
)
const (
defaultConfigDir = "config"
informersConfigFile = "informers.yaml"
)
var (
errNoConfig = errors.New("no informer configurations provided")
errNoContext = errors.New("no kubeconfig contexts provided")
errNoServices = errors.New("no services listed in informer configuration")
errDuplicateContext = errors.New("context name used more than once in the informer configuration")
errDuplicateNamespace = errors.New("namespace used more than once in the informer configuration")
)
func Kubecontexts(logger logr.Logger) ([]informers.Kubecontext, error) {
configDir, exists := os.LookupEnv("CONFIG_DIR")
if !exists {
configDir = defaultConfigDir
}
informersConfigFilePath := filepath.Join(configDir, informersConfigFile)
logger.V(4).Info("Loading informer configuration", "filepath", informersConfigFilePath)
yamlBytes, err := os.ReadFile(informersConfigFilePath)
if err != nil {
return nil, fmt.Errorf("could not read informer configurations from file %s: %w", informersConfigFilePath, err)
}
var kubecontexts []informers.Kubecontext
err = yaml.Unmarshal(yamlBytes, &kubecontexts)
if err != nil {
return nil, fmt.Errorf("could not unmarshal informer configuration YAML file contents [%s]: %w", yamlBytes, err)
}
if err := validateKubeContexts(kubecontexts); err != nil {
return nil, fmt.Errorf("informer configuration validation failed: %w", err)
}
logger.V(2).Info("Informer", "configurations", kubecontexts)
return kubecontexts, err
}
func validateKubeContexts(contexts []informers.Kubecontext) error {
if len(contexts) == 0 {
return errNoContext
}
contextNames := map[string]bool{}
for _, context := range contexts {
if _, exists := contextNames[context.Context]; exists {
return fmt.Errorf("%w: context=%s", errDuplicateContext, context.Context)
}
if err := validateInformerConfigs(context.Informers); err != nil {
return fmt.Errorf("invalid informer config for context=%s: %w", context.Context, err)
}
contextNames[context.Context] = true
}
return nil
}
func validateInformerConfigs(configs []informers.Config) error {
if len(configs) == 0 {
return errNoConfig
}
namespaces := map[string]bool{}
for _, config := range configs {
if config.Services == nil || len(config.Services) == 0 {
return fmt.Errorf("%w: config=%+v", errNoServices, config)
}
if _, exists := namespaces[config.Namespace]; exists {
return fmt.Errorf("%w: namespace=%s", errDuplicateNamespace, config.Namespace)
}
namespaces[config.Namespace] = true
}
return nil
}