diff --git a/cmd/create/createCluster.go b/cmd/create/createCluster.go index 5ecfd84e..1bfc9aa1 100644 --- a/cmd/create/createCluster.go +++ b/cmd/create/createCluster.go @@ -26,6 +26,7 @@ import ( "github.com/spf13/cobra" + "github.com/rancher/k3d/pkg/cluster" k3dCluster "github.com/rancher/k3d/pkg/cluster" "github.com/rancher/k3d/pkg/runtimes" k3d "github.com/rancher/k3d/pkg/types" @@ -58,6 +59,7 @@ func NewCmdCreateCluster() *cobra.Command { cmd.Flags().String("image", k3d.DefaultK3sImageRepo, "Specify k3s image that you want to use for the nodes") // TODO: get image version cmd.Flags().String("network", "", "Join an existing network") cmd.Flags().String("secret", "", "Specify a cluster secret. By default, we generate one.") + cmd.Flags().StringSliceP("volume", "v", nil, "Mount volumes into the nodes (Format: `--volume [SOURCE:]DEST[@SELECTOR[@SELECTOR...]]`") // add subcommands @@ -113,12 +115,22 @@ func parseCreateClusterCmd(cmd *cobra.Command, args []string) (runtimes.Runtime, log.Fatalln(err) } - // --api-port // TODO: + // --api-port apiPort, err := cmd.Flags().GetString("api-port") if err != nil { log.Fatalln(err) } + // --volume + volumes, err := cmd.Flags().GetStringSlice("volume") + if err != nil { + log.Fatalln(err) + } + if err := cluster.ValidateVolumeFlag(volumes); err != nil { // TODO: also split SPECIFIER from here and create map from what mount where + log.Errorln("Failed to validate '--volume' flag") + log.Fatalln(err) + } + /* generate cluster */ cluster := &k3d.Cluster{ Name: args[0], // TODO: validate name @@ -132,8 +144,10 @@ func parseCreateClusterCmd(cmd *cobra.Command, args []string) (runtimes.Runtime, // -> master nodes for i := 0; i < masterCount; i++ { node := k3d.Node{ - Role: k3d.MasterRole, - Image: image, + Role: k3d.MasterRole, + Image: image, + Volumes: volumes, // add only volumes for `all`, `masters`, `master[i]` and `k3d--master-` + Labels: make(map[string]string, 1), } if i == 0 { node.Ports = append(node.Ports, fmt.Sprintf("0.0.0.0:%s:6443/tcp", apiPort)) // TODO: update (choose interface, enable more than one master) and get '6443' from defaultport variable @@ -145,8 +159,9 @@ func parseCreateClusterCmd(cmd *cobra.Command, args []string) (runtimes.Runtime, // -> worker nodes for i := 0; i < workerCount; i++ { node := k3d.Node{ - Role: k3d.WorkerRole, - Image: image, + Role: k3d.WorkerRole, + Image: image, + Volumes: volumes, // add only volumes for `all`, `workers`, `worker[i]` and `k3d--worker-` } cluster.Nodes = append(cluster.Nodes, node) } diff --git a/pkg/cluster/volumes.go b/pkg/cluster/volumes.go new file mode 100644 index 00000000..dcb28ed8 --- /dev/null +++ b/pkg/cluster/volumes.go @@ -0,0 +1,60 @@ +/* +Copyright © 2019 Thorsten Klein + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package cluster + +import ( + "fmt" + "os" + "strings" +) + +// ValidateVolumeFlag checks, if the source of volume mounts exists and if the destination is an absolute path +func ValidateVolumeFlag(volumes []string) error { + + for _, volume := range volumes { + src := "" + dest := "" + + split := strings.Split(volume, ":") + if len(split) < 1 || len(split) > 2 { + return fmt.Errorf("Invalid volume mount '%s'", volume) + } + if len(split) == 1 { + dest = split[0] + } else { + src = split[0] + dest = split[1] + } + + if src != "" { + if _, err := os.Stat(src); err != nil { + return fmt.Errorf("Failed to stat file/dir that you're trying to mount: '%s' in '%s'", src, volume) + } + } + + if !strings.HasPrefix(dest, "/") { + return fmt.Errorf("Volume mount destination doesn't appear to be an absolute path: '%s' in '%s'", dest, volume) + } + + } + return nil +}