add image volume

This commit is contained in:
iwilltry42 2019-12-04 08:19:42 +01:00
parent 60659c74c8
commit e6d7726ffb
9 changed files with 216 additions and 13 deletions

View File

@ -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

View File

@ -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)
}, },
} }

View File

@ -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)
} }

View File

@ -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
} }

View 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
}

View 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
}

View File

@ -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)

View File

@ -72,6 +72,14 @@ 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"`
@ -81,6 +89,7 @@ type Cluster struct {
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

View File

@ -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