mirror of
https://github.com/open-cluster-management-io/ocm.git
synced 2026-05-13 04:38:13 +00:00
139 lines
4.8 KiB
Go
139 lines
4.8 KiB
Go
package controllercmd
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/openshift/library-go/pkg/config/client"
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
"k8s.io/apimachinery/pkg/runtime/serializer"
|
|
kyaml "k8s.io/apimachinery/pkg/util/yaml"
|
|
"k8s.io/client-go/rest"
|
|
)
|
|
|
|
// ControllerFlags provides the "normal" controller flags
|
|
type ControllerFlags struct {
|
|
// ConfigFile hold the configfile to load
|
|
ConfigFile string
|
|
// KubeConfigFile points to a kubeconfig file if you don't want to use the in cluster config
|
|
KubeConfigFile string
|
|
// Namespace points to a base namespace for the controller and related events
|
|
Namespace string
|
|
// BindAddress is the ip:port to serve on
|
|
BindAddress string
|
|
// TerminateOnFiles is a list of files. If any of these changes, the process terminates.
|
|
TerminateOnFiles []string
|
|
}
|
|
|
|
// NewControllerFlags returns flags with default values set
|
|
func NewControllerFlags() *ControllerFlags {
|
|
return &ControllerFlags{}
|
|
}
|
|
|
|
// Validate makes sure the required flags are specified and no illegal combinations are found
|
|
func (o *ControllerFlags) Validate() error {
|
|
// everything is optional currently
|
|
return nil
|
|
}
|
|
|
|
// AddFlags register and binds the default flags
|
|
func (f *ControllerFlags) AddFlags(cmd *cobra.Command) {
|
|
flags := cmd.Flags()
|
|
// This command only supports reading from config
|
|
flags.StringVar(&f.ConfigFile, "config", f.ConfigFile, "Location of the master configuration file to run from.")
|
|
cmd.MarkFlagFilename("config", "yaml", "yml")
|
|
flags.StringVar(&f.KubeConfigFile, "kubeconfig", f.KubeConfigFile, "Location of the master configuration file to run from.")
|
|
cmd.MarkFlagFilename("kubeconfig", "kubeconfig")
|
|
flags.StringVar(&f.Namespace, "namespace", f.Namespace, "Namespace where the controller is running. Auto-detected if run in cluster.")
|
|
flags.StringVar(&f.BindAddress, "listen", f.BindAddress, "The ip:port to serve on.")
|
|
flags.StringArrayVar(&f.TerminateOnFiles, "terminate-on-files", f.TerminateOnFiles, "A list of files. If one of them changes, the process will terminate.")
|
|
}
|
|
|
|
// ToConfigObj given completed flags, returns a config object for the flag that was specified.
|
|
// TODO versions goes away in 1.11
|
|
func (f *ControllerFlags) ToConfigObj() ([]byte, *unstructured.Unstructured, error) {
|
|
// no file means empty, not err
|
|
if len(f.ConfigFile) == 0 {
|
|
return nil, nil, nil
|
|
}
|
|
|
|
content, err := ioutil.ReadFile(f.ConfigFile)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
// empty file means empty, not err
|
|
if len(content) == 0 {
|
|
return nil, nil, err
|
|
}
|
|
|
|
data, err := kyaml.ToJSON(content)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, data)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return content, uncastObj.(*unstructured.Unstructured), nil
|
|
}
|
|
|
|
// ToClientConfig given completed flags, returns a rest.Config. overrides are optional
|
|
func (f *ControllerFlags) ToClientConfig(overrides *client.ClientConnectionOverrides) (*rest.Config, error) {
|
|
return client.GetKubeConfigOrInClusterConfig(f.KubeConfigFile, overrides)
|
|
}
|
|
|
|
// ReadYAML decodes a runtime.Object from the provided scheme
|
|
// TODO versions goes away with more complete scheme in 1.11
|
|
func ReadYAML(data []byte, configScheme *runtime.Scheme, versions ...schema.GroupVersion) (runtime.Object, error) {
|
|
data, err := kyaml.ToJSON(data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
configCodecFactory := serializer.NewCodecFactory(configScheme)
|
|
obj, err := runtime.Decode(configCodecFactory.UniversalDecoder(versions...), data)
|
|
if err != nil {
|
|
return nil, captureSurroundingJSONForError("error reading config: ", data, err)
|
|
}
|
|
return obj, err
|
|
}
|
|
|
|
// ReadYAMLFile read a file and decodes a runtime.Object from the provided scheme
|
|
func ReadYAMLFile(filename string, configScheme *runtime.Scheme, versions ...schema.GroupVersion) (runtime.Object, error) {
|
|
data, err := ioutil.ReadFile(filename)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
obj, err := ReadYAML(data, configScheme, versions...)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not load config file %q due to an error: %v", filename, err)
|
|
}
|
|
return obj, err
|
|
}
|
|
|
|
// TODO: we ultimately want a better decoder for JSON that allows us exact line numbers and better
|
|
// surrounding text description. This should be removed / replaced when that happens.
|
|
func captureSurroundingJSONForError(prefix string, data []byte, err error) error {
|
|
if syntaxErr, ok := err.(*json.SyntaxError); err != nil && ok {
|
|
offset := syntaxErr.Offset
|
|
begin := offset - 20
|
|
if begin < 0 {
|
|
begin = 0
|
|
}
|
|
end := offset + 20
|
|
if end > int64(len(data)) {
|
|
end = int64(len(data))
|
|
}
|
|
return fmt.Errorf("%s%v (found near '%s')", prefix, err, string(data[begin:end]))
|
|
}
|
|
if err != nil {
|
|
return fmt.Errorf("%s%v", prefix, err)
|
|
}
|
|
return err
|
|
}
|