add image volume
This commit is contained in:
parent
60659c74c8
commit
e6d7726ffb
@ -64,6 +64,9 @@ func NewCmdCreateCluster() *cobra.Command {
|
|||||||
cmd.Flags().StringArrayP("volume", "v", nil, "Mount volumes into the nodes (Format: `--volume [SOURCE:]DEST[@NODEFILTER[;NODEFILTER...]]`")
|
cmd.Flags().StringArrayP("volume", "v", nil, "Mount volumes into the nodes (Format: `--volume [SOURCE:]DEST[@NODEFILTER[;NODEFILTER...]]`")
|
||||||
cmd.Flags().StringArrayP("port", "p", nil, "Map ports from the node containers to the host (Format: `[HOST:][HOSTPORT:]CONTAINERPORT[/PROTOCOL][@NODEFILTER]`)")
|
cmd.Flags().StringArrayP("port", "p", nil, "Map ports from the node containers to the host (Format: `[HOST:][HOSTPORT:]CONTAINERPORT[/PROTOCOL][@NODEFILTER]`)")
|
||||||
|
|
||||||
|
/* Image Importing */
|
||||||
|
cmd.Flags().Bool("no-image-volume", false, "Disable the creation of a volume for importing images")
|
||||||
|
|
||||||
/* Multi Master Configuration */ // TODO: to implement (whole multi master thingy)
|
/* Multi Master Configuration */ // TODO: to implement (whole multi master thingy)
|
||||||
// multi-master - general
|
// multi-master - general
|
||||||
cmd.Flags().Bool("no-lb", false, "Disable automatic deployment of a load balancer in Multi-Master setups")
|
cmd.Flags().Bool("no-lb", false, "Disable automatic deployment of a load balancer in Multi-Master setups")
|
||||||
@ -277,11 +280,22 @@ func parseCreateClusterCmd(cmd *cobra.Command, args []string) (runtimes.Runtime,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* generate cluster */
|
// --no-image-volume
|
||||||
|
noImageVolume, err := cmd.Flags().GetBool("no-image-volume")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************
|
||||||
|
* generate cluster *
|
||||||
|
********************/
|
||||||
cluster := &k3d.Cluster{
|
cluster := &k3d.Cluster{
|
||||||
Name: args[0], // TODO: validate name0
|
Name: args[0], // TODO: validate name0
|
||||||
Network: network,
|
Network: network,
|
||||||
Secret: secret,
|
Secret: secret,
|
||||||
|
ClusterCreationOpts: &k3d.ClusterCreationOpts{
|
||||||
|
DisableImageVolume: noImageVolume,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate list of nodes
|
// generate list of nodes
|
||||||
|
@ -38,9 +38,10 @@ func NewCmdLoadImage() *cobra.Command {
|
|||||||
Use: "image",
|
Use: "image",
|
||||||
Short: "Load an image from docker into a k3d cluster.",
|
Short: "Load an image from docker into a k3d cluster.",
|
||||||
Long: `Load an image from docker into a k3d cluster.`,
|
Long: `Load an image from docker into a k3d cluster.`,
|
||||||
|
Args: cobra.MinimumNArgs(1),
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
runtime, images, clusters := parseLoadImageCmd(cmd, args)
|
runtime, images, clusters := parseLoadImageCmd(cmd, args)
|
||||||
log.Debugln("Load Images not yet implemented: ", runtime, images, clusters)
|
log.Debugf("Load images [%+v] from runtime [%s] into clusters [%+v]", runtime, images, clusters)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +70,25 @@ func CreateCluster(cluster *k3d.Cluster, runtime k3drt.Runtime) error {
|
|||||||
cluster.Secret = GenerateClusterSecret()
|
cluster.Secret = GenerateClusterSecret()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Cluster-Wide volumes
|
||||||
|
* - image volume (for importing images)
|
||||||
|
*/
|
||||||
|
if !cluster.ClusterCreationOpts.DisableImageVolume {
|
||||||
|
imageVolumeName := fmt.Sprintf("%s-images", cluster.Name)
|
||||||
|
if err := runtime.CreateVolume(imageVolumeName, map[string]string{"k3d.cluster": cluster.Name}); err != nil {
|
||||||
|
log.Errorln("Failed to create image volume '%s' for cluster '%s'", imageVolumeName, cluster.Name)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
extraLabels["k3d.cluster.volumes.imagevolume"] = imageVolumeName
|
||||||
|
|
||||||
|
// attach volume to nodes
|
||||||
|
for _, node := range cluster.Nodes {
|
||||||
|
node.Volumes = append(node.Volumes, fmt.Sprintf("%s:%s", imageVolumeName, k3d.DefaultImageVolumeMountPath))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Nodes
|
* Nodes
|
||||||
*/
|
*/
|
||||||
@ -209,6 +228,15 @@ func DeleteCluster(cluster *k3d.Cluster, runtime k3drt.Runtime) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// delete image volume
|
||||||
|
if imagevolume, ok := cluster.Nodes[0].Labels["k3d.cluster.volumes.imagevolume"]; ok {
|
||||||
|
log.Infof("Deleting image volume '%s'", imagevolume)
|
||||||
|
if err := runtime.DeleteVolume(imagevolume); err != nil {
|
||||||
|
log.Warningf("Failed to delete image volume '%s' of cluster '%s': Try to delete it manually", cluster.Name, imagevolume)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return error if we failed to delete a node
|
||||||
if failed > 0 {
|
if failed > 0 {
|
||||||
return fmt.Errorf("Failed to delete %d nodes: Try to delete them manually", failed)
|
return fmt.Errorf("Failed to delete %d nodes: Try to delete them manually", failed)
|
||||||
}
|
}
|
||||||
|
@ -22,12 +22,20 @@ THE SOFTWARE.
|
|||||||
package cluster
|
package cluster
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/rancher/k3d/pkg/runtimes"
|
||||||
k3d "github.com/rancher/k3d/pkg/types"
|
k3d "github.com/rancher/k3d/pkg/types"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StartToolsContainer will start a new k3d tools container and connect it to the network of the chosen cluster
|
// LoadImagesIntoCluster starts up a k3d tools container for the selected clusters and uses it to export
|
||||||
func StartToolsContainer(cluster *k3d.Cluster) error {
|
// images from the runtime to import them into the nodes of the selected cluster
|
||||||
log.Debugln("starttoolscontainer")
|
func LoadImagesIntoCluster(runtime runtimes.Runtime, images []string, clusters *k3d.Cluster) {
|
||||||
return nil
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// startToolsContainer will start a new k3d tools container and connect it to the network of the chosen cluster
|
||||||
|
func startToolsContainer(runtime runtimes.Runtime, cluster *k3d.Cluster) (string, error) {
|
||||||
|
log.Debugln("starttoolscontainer")
|
||||||
|
|
||||||
|
return "", nil
|
||||||
}
|
}
|
||||||
|
32
pkg/runtimes/containerd/volume.go
Normal file
32
pkg/runtimes/containerd/volume.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2019 Thorsten Klein <iwilltry42@gmail.com>
|
||||||
|
|
||||||
|
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 containerd
|
||||||
|
|
||||||
|
// CreateVolume creates a new named volume
|
||||||
|
func (d Containerd) CreateVolume(name string, labels map[string]string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteVolume creates a new named volume
|
||||||
|
func (d Containerd) DeleteVolume(name string) error {
|
||||||
|
return nil
|
||||||
|
}
|
96
pkg/runtimes/docker/volume.go
Normal file
96
pkg/runtimes/docker/volume.go
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2019 Thorsten Klein <iwilltry42@gmail.com>
|
||||||
|
|
||||||
|
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 docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/volume"
|
||||||
|
"github.com/docker/docker/client"
|
||||||
|
k3d "github.com/rancher/k3d/pkg/types"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateVolume creates a new named volume
|
||||||
|
func (d Docker) CreateVolume(name string, labels map[string]string) error {
|
||||||
|
// (0) create new docker client
|
||||||
|
ctx := context.Background()
|
||||||
|
docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("Failed to create docker client")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// (1) create volume
|
||||||
|
volumeCreateOptions := volume.VolumeCreateBody{
|
||||||
|
Name: name,
|
||||||
|
Labels: k3d.DefaultObjectLabels,
|
||||||
|
Driver: "local", // TODO: allow setting driver + opts
|
||||||
|
DriverOpts: map[string]string{},
|
||||||
|
}
|
||||||
|
for k, v := range labels {
|
||||||
|
volumeCreateOptions.Labels[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
vol, err := docker.VolumeCreate(ctx, volumeCreateOptions)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Failed to create volume '%s'", name)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Infof("Created volume '%s'", vol.Name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteVolume creates a new named volume
|
||||||
|
func (d Docker) DeleteVolume(name string) error {
|
||||||
|
// (0) create new docker client
|
||||||
|
ctx := context.Background()
|
||||||
|
docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln("Failed to create docker client")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// get volume and delete it
|
||||||
|
vol, err := docker.VolumeInspect(ctx, name)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Failed to find volume '%s'", name)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if volume is still in use
|
||||||
|
if vol.UsageData != nil {
|
||||||
|
if vol.UsageData.RefCount > 0 {
|
||||||
|
log.Errorf("Failed to delete volume '%s'")
|
||||||
|
return fmt.Errorf("Volume '%s' is still referenced by %d containers", name, vol.UsageData.RefCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove volume
|
||||||
|
if err := docker.VolumeRemove(ctx, name, true); err != nil {
|
||||||
|
log.Errorf("Failed to delete volume '%s'", name)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -47,6 +47,8 @@ type Runtime interface {
|
|||||||
DeleteNetwork(ID string) error
|
DeleteNetwork(ID string) error
|
||||||
StartNode(*k3d.Node) error
|
StartNode(*k3d.Node) error
|
||||||
StopNode(*k3d.Node) error
|
StopNode(*k3d.Node) error
|
||||||
|
CreateVolume(string, map[string]string) error
|
||||||
|
DeleteVolume(string) error
|
||||||
// ExecContainer() error
|
// ExecContainer() error
|
||||||
// DeleteContainer() error
|
// DeleteContainer() error
|
||||||
GetNodeLogs(*k3d.Node) (io.ReadCloser, error)
|
GetNodeLogs(*k3d.Node) (io.ReadCloser, error)
|
||||||
|
@ -72,15 +72,24 @@ var DefaultNodeEnv = []string{
|
|||||||
"K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml",
|
"K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DefaultImageVolumeMountPath defines the mount path inside k3d nodes where we will mount the shared image volume by default
|
||||||
|
const DefaultImageVolumeMountPath = "/k3d/images"
|
||||||
|
|
||||||
|
// ClusterCreationOpts describe a set of options one can set when creating a cluster
|
||||||
|
type ClusterCreationOpts struct {
|
||||||
|
DisableImageVolume bool
|
||||||
|
}
|
||||||
|
|
||||||
// Cluster describes a k3d cluster
|
// Cluster describes a k3d cluster
|
||||||
type Cluster struct {
|
type Cluster struct {
|
||||||
Name string `yaml:"name" json:"name,omitempty"`
|
Name string `yaml:"name" json:"name,omitempty"`
|
||||||
Network string `yaml:"network" json:"network,omitempty"`
|
Network string `yaml:"network" json:"network,omitempty"`
|
||||||
Secret string `yaml:"cluster_secret" json:"clusterSecret,omitempty"`
|
Secret string `yaml:"cluster_secret" json:"clusterSecret,omitempty"`
|
||||||
Nodes []*Node `yaml:"nodes" json:"nodes,omitempty"`
|
Nodes []*Node `yaml:"nodes" json:"nodes,omitempty"`
|
||||||
InitNode *Node // init master node
|
InitNode *Node // init master node
|
||||||
MasterLoadBalancer *ClusterLoadbalancer `yaml:"master_loadbalancer" json:"masterLoadBalancer,omitempty"`
|
MasterLoadBalancer *ClusterLoadbalancer `yaml:"master_loadbalancer" json:"masterLoadBalancer,omitempty"`
|
||||||
ExternalDatastore ExternalDatastore `yaml:"external_datastore" json:"externalDatastore,omitempty"`
|
ExternalDatastore ExternalDatastore `yaml:"external_datastore" json:"externalDatastore,omitempty"`
|
||||||
|
ClusterCreationOpts *ClusterCreationOpts `yaml:"options" json:"options,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Node describes a k3d node
|
// Node describes a k3d node
|
||||||
|
13
thoughts.md
13
thoughts.md
@ -163,6 +163,7 @@ Here's how k3d types should translate to a runtime type:
|
|||||||
- cluster NAME
|
- cluster NAME
|
||||||
- --all
|
- --all
|
||||||
- node NAME
|
- node NAME
|
||||||
|
- --all
|
||||||
- get
|
- get
|
||||||
- cluster NAME
|
- cluster NAME
|
||||||
- --no-headers
|
- --no-headers
|
||||||
@ -178,3 +179,15 @@ Here's how k3d types should translate to a runtime type:
|
|||||||
- cluster NAME
|
- cluster NAME
|
||||||
- --all
|
- --all
|
||||||
- node NAME
|
- node NAME
|
||||||
|
|
||||||
|
## tools
|
||||||
|
|
||||||
|
- maybe rename `k3d load` to `k3d tools` and add tool cmds there?
|
||||||
|
- e.g. `k3d tools import-images`
|
||||||
|
- let's you set tools container version
|
||||||
|
- `k3d tools --image k3d-tools:v2 import-images`
|
||||||
|
|
||||||
|
## extra commands
|
||||||
|
|
||||||
|
- `k3d prune` to prune all dangling resources
|
||||||
|
- nodes, volumes, networks
|
||||||
|
Loading…
Reference in New Issue
Block a user