diff --git a/README.md b/README.md index 5e048d19900ae4e4cdec0dd41e8ac16195bc615a..69cf297a7cc2ee90457751d7dae416a0176e3261 100644 --- a/README.md +++ b/README.md @@ -188,6 +188,25 @@ To uninstall, execute: kubectl delete -f https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml ``` +## Debug +> it providers a out-of-cluster debug env for deverlopers +### debug +```Bash +git clone https://github.com/rancher/local-path-provisioner.git +cd local-path-provisioner +go build +kubectl apply -f debug/config.yaml +./local-path-provisioner --debug start --namespace=local-path-storage +``` + +### example +[Usage](#usage) + +### clear +``` +kubectl delete -f debug/config.yaml +``` + ## License Copyright (c) 2014-2020 [Rancher Labs, Inc.](http://rancher.com/) diff --git a/debug/config.yaml b/debug/config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b1c096122ef10147f66ceb2bac9af5b24dddf1bf --- /dev/null +++ b/debug/config.yaml @@ -0,0 +1,48 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: local-path-storage +--- + +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: local-path +provisioner: rancher.io/local-path +volumeBindingMode: WaitForFirstConsumer +reclaimPolicy: Delete + +--- + +kind: ConfigMap +apiVersion: v1 +metadata: + name: local-path-config + namespace: local-path-storage +data: + config.json: |- + { + "nodePathMap":[ + { + "node":"DEFAULT_PATH_FOR_NON_LISTED_NODES", + "paths":["/opt/local-path-provisioner"] + }, + { + "node":"yasker-lp-dev1", + "paths":["/opt/local-path-provisioner", "/data1"] + }, + { + "node":"yasker-lp-dev3", + "paths":[] + } + ] + } + setup: |- + #!/bin/sh + path=$1 + mkdir -m 0777 -p ${path} + teardown: |- + #!/bin/sh + path=$1 + rm -rf ${path} + diff --git a/main.go b/main.go index 4f15bea71f26cca197c9b1fd648c2bf6449c8f3a..fd4792f9fcbf998c6ec0966a1e46a5e17ff59a9b 100644 --- a/main.go +++ b/main.go @@ -4,30 +4,37 @@ import ( "fmt" "os" "os/signal" + "path/filepath" "syscall" "github.com/Sirupsen/logrus" "github.com/pkg/errors" "github.com/urfave/cli" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" clientset "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" pvController "sigs.k8s.io/sig-storage-lib-external-provisioner/controller" ) var ( VERSION = "0.0.1" - FlagConfigFile = "config" - FlagProvisionerName = "provisioner-name" - EnvProvisionerName = "PROVISIONER_NAME" - DefaultProvisionerName = "rancher.io/local-path" - FlagNamespace = "namespace" - EnvNamespace = "POD_NAMESPACE" - DefaultNamespace = "local-path-storage" - FlagHelperImage = "helper-image" - EnvHelperImage = "HELPER_IMAGE" - DefaultHelperImage = "busybox" + FlagConfigFile = "config" + FlagProvisionerName = "provisioner-name" + EnvProvisionerName = "PROVISIONER_NAME" + DefaultProvisionerName = "rancher.io/local-path" + FlagNamespace = "namespace" + EnvNamespace = "POD_NAMESPACE" + DefaultNamespace = "local-path-storage" + FlagHelperImage = "helper-image" + EnvHelperImage = "HELPER_IMAGE" + DefaultHelperImage = "busybox" + FlagKubeconfig = "kubeconfig" + DefaultKubeConfigFilePath = ".kube/config" + DefaultConfigFileKey = "config.json" + DefaultConfigMapName = "local-path-config" ) func cmdNotFound(c *cli.Context, command string) { @@ -75,6 +82,11 @@ func StartCmd() cli.Command { EnvVar: EnvHelperImage, Value: DefaultHelperImage, }, + cli.StringFlag{ + Name: FlagKubeconfig, + Usage: "Paths to a kubeconfig. Only required when it is out-of-cluster.", + Value: "", + }, }, Action: func(c *cli.Context) { if err := startDaemon(c); err != nil { @@ -84,11 +96,45 @@ func StartCmd() cli.Command { } } +func homeDir() string { + if h := os.Getenv("HOME"); h != "" { + return h + } + return os.Getenv("USERPROFILE") // windows +} + +func loadConfig(kubeconfig string) (*rest.Config, error) { + if c, err := rest.InClusterConfig(); err == nil { + return c, nil + } + home := homeDir() + if kubeconfig == "" && home != "" { + kubeconfig = filepath.Join(home, DefaultKubeConfigFilePath) + } + _, err := os.Stat(kubeconfig) + if err != nil { + return nil, err + } + return clientcmd.BuildConfigFromFlags("", kubeconfig) +} + +func findConfigFileFromConfigMap(kubeClient clientset.Interface, namespace string) (string, error) { + cm, err := kubeClient.CoreV1().ConfigMaps(namespace).Get(DefaultConfigMapName, metav1.GetOptions{}) + if err != nil { + return "", err + } + configFile, ok := cm.Data[DefaultConfigFileKey] + if !ok { + return "", fmt.Errorf("%v is not exist in local-path-config ConfigMap", DefaultConfigFileKey) + } + return configFile, nil +} + func startDaemon(c *cli.Context) error { stopCh := make(chan struct{}) RegisterShutdownChannel(stopCh) - config, err := rest.InClusterConfig() + config, err := loadConfig(c.String(FlagKubeconfig)) if err != nil { return errors.Wrap(err, "unable to get client config") } @@ -103,10 +149,6 @@ func startDaemon(c *cli.Context) error { return errors.Wrap(err, "Cannot start Provisioner: failed to get Kubernetes server version") } - configFile := c.String(FlagConfigFile) - if configFile == "" { - return fmt.Errorf("invalid empty flag %v", FlagConfigFile) - } provisionerName := c.String(FlagProvisionerName) if provisionerName == "" { return fmt.Errorf("invalid empty flag %v", FlagProvisionerName) @@ -115,6 +157,13 @@ func startDaemon(c *cli.Context) error { if namespace == "" { return fmt.Errorf("invalid empty flag %v", FlagNamespace) } + configFile := c.String(FlagConfigFile) + if configFile == "" { + configFile, err = findConfigFileFromConfigMap(kubeClient, namespace) + if err != nil { + return fmt.Errorf("invalid empty flag %v and it also does not exist at ConfigMap %v/%v", FlagConfigFile, namespace, DefaultConfigMapName) + } + } helperImage := c.String(FlagHelperImage) if helperImage == "" { return fmt.Errorf("invalid empty flag %v", FlagHelperImage) diff --git a/provisioner.go b/provisioner.go index 4a2ea1706b82f50c885cab97f6f4af474678c83f..e80e8cc8210ff85a4b3a755dbaa1f576604321bf 100644 --- a/provisioner.go +++ b/provisioner.go @@ -418,10 +418,27 @@ func (p *LocalPathProvisioner) createHelperPod(action ActionType, cmdsForPath [] return nil } +func isJSONFile(configFile string) bool { + return strings.HasSuffix(configFile, ".json") +} + +func unmarshalFromString(configFile string) (*ConfigData, error) { + var data ConfigData + if err := json.Unmarshal([]byte(configFile), &data); err != nil { + return nil, err + } + return &data, nil +} + func loadConfigFile(configFile string) (cfgData *ConfigData, err error) { defer func() { err = errors.Wrapf(err, "fail to load config file %v", configFile) }() + + if !isJSONFile(configFile) { + return unmarshalFromString(configFile) + } + f, err := os.Open(configFile) if err != nil { return nil, err