Files
open-cluster-management/vendor/github.com/openshift/library-go/pkg/controller/controllercmd/flags.go
2020-04-29 15:29:31 +08:00

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
}