180 lines
5.0 KiB
Go
180 lines
5.0 KiB
Go
package run
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/api/types/container"
|
|
"github.com/docker/docker/api/types/filters"
|
|
"github.com/docker/docker/api/types/volume"
|
|
"github.com/docker/docker/client"
|
|
)
|
|
|
|
type Volumes struct {
|
|
DefaultVolumes []string
|
|
NodeSpecificVolumes map[string][]string
|
|
GroupSpecificVolumes map[string][]string
|
|
}
|
|
|
|
// createImageVolume will create a new docker volume used for storing image tarballs that can be loaded into the clusters
|
|
func createImageVolume(clusterName string) (types.Volume, error) {
|
|
|
|
var vol types.Volume
|
|
|
|
ctx := context.Background()
|
|
docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
|
if err != nil {
|
|
return vol, fmt.Errorf(" Couldn't create docker client\n%+v", err)
|
|
}
|
|
|
|
volName := fmt.Sprintf("k3d-%s-images", clusterName)
|
|
|
|
volumeCreateOptions := volume.VolumeCreateBody{
|
|
Name: volName,
|
|
Labels: map[string]string{
|
|
"app": "k3d",
|
|
"cluster": clusterName,
|
|
},
|
|
Driver: "local", //TODO: allow setting driver + opts
|
|
DriverOpts: map[string]string{},
|
|
}
|
|
vol, err = docker.VolumeCreate(ctx, volumeCreateOptions)
|
|
if err != nil {
|
|
return vol, fmt.Errorf("failed to create image volume [%s] for cluster [%s]\n%+v", volName, clusterName, err)
|
|
}
|
|
|
|
return vol, nil
|
|
}
|
|
|
|
// deleteImageVolume will delete the volume we created for sharing images with this cluster
|
|
func deleteImageVolume(clusterName string) error {
|
|
|
|
ctx := context.Background()
|
|
docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
|
if err != nil {
|
|
return fmt.Errorf(" Couldn't create docker client\n%+v", err)
|
|
}
|
|
|
|
volName := fmt.Sprintf("k3d-%s-images", clusterName)
|
|
|
|
if err = docker.VolumeRemove(ctx, volName, true); err != nil {
|
|
return fmt.Errorf(" Couldn't remove volume [%s] for cluster [%s]\n%+v", volName, clusterName, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// getImageVolume returns the docker volume object representing the imagevolume for the cluster
|
|
func getImageVolume(clusterName string) (types.Volume, error) {
|
|
var vol types.Volume
|
|
volName := fmt.Sprintf("k3d-%s-images", clusterName)
|
|
|
|
ctx := context.Background()
|
|
docker, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
|
if err != nil {
|
|
return vol, fmt.Errorf(" Couldn't create docker client\n%+v", err)
|
|
}
|
|
|
|
filters := filters.NewArgs()
|
|
filters.Add("label", "app=k3d")
|
|
filters.Add("label", fmt.Sprintf("cluster=%s", clusterName))
|
|
volumeList, err := docker.VolumeList(ctx, filters)
|
|
if err != nil {
|
|
return vol, fmt.Errorf(" Couldn't get volumes for cluster [%s]\n%+v ", clusterName, err)
|
|
}
|
|
volFound := false
|
|
for _, volume := range volumeList.Volumes {
|
|
if volume.Name == volName {
|
|
vol = *volume
|
|
volFound = true
|
|
break
|
|
}
|
|
}
|
|
if !volFound {
|
|
return vol, fmt.Errorf("didn't find volume [%s] in list of volumes returned for cluster [%s]", volName, clusterName)
|
|
}
|
|
|
|
return vol, nil
|
|
}
|
|
|
|
func NewVolumes(volumes []string) (*Volumes, error) {
|
|
volumesSpec := &Volumes{
|
|
DefaultVolumes: []string{},
|
|
NodeSpecificVolumes: make(map[string][]string),
|
|
GroupSpecificVolumes: make(map[string][]string),
|
|
}
|
|
|
|
volumes:
|
|
for _, volume := range volumes {
|
|
if strings.Contains(volume, "@") {
|
|
split := strings.Split(volume, "@")
|
|
if len(split) != 2 {
|
|
return nil, fmt.Errorf("invalid node volume spec: %s", volume)
|
|
}
|
|
|
|
nodeVolumes := split[0]
|
|
node := strings.ToLower(split[1])
|
|
if len(node) == 0 {
|
|
return nil, fmt.Errorf("invalid node volume spec: %s", volume)
|
|
}
|
|
|
|
// check if node selector is a node group
|
|
for group, names := range nodeRuleGroupsMap {
|
|
added := false
|
|
|
|
for _, name := range names {
|
|
if name == node {
|
|
volumesSpec.addGroupSpecificVolume(group, nodeVolumes)
|
|
added = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if added {
|
|
continue volumes
|
|
}
|
|
}
|
|
|
|
// otherwise this is a volume for a specific node
|
|
volumesSpec.addNodeSpecificVolume(node, nodeVolumes)
|
|
} else {
|
|
volumesSpec.DefaultVolumes = append(volumesSpec.DefaultVolumes, volume)
|
|
}
|
|
}
|
|
|
|
return volumesSpec, nil
|
|
}
|
|
|
|
// addVolumesToHostConfig adds all default volumes and node / group specific volumes to a HostConfig
|
|
func (v Volumes) addVolumesToHostConfig(containerName string, groupName string, hostConfig *container.HostConfig) {
|
|
volumes := v.DefaultVolumes
|
|
|
|
if v, ok := v.NodeSpecificVolumes[containerName]; ok {
|
|
volumes = append(volumes, v...)
|
|
}
|
|
|
|
if v, ok := v.GroupSpecificVolumes[groupName]; ok {
|
|
volumes = append(volumes, v...)
|
|
}
|
|
|
|
if len(volumes) > 0 {
|
|
hostConfig.Binds = volumes
|
|
}
|
|
}
|
|
|
|
func (v *Volumes) addNodeSpecificVolume(node, volume string) {
|
|
if _, ok := v.NodeSpecificVolumes[node]; !ok {
|
|
v.NodeSpecificVolumes[node] = []string{}
|
|
}
|
|
v.NodeSpecificVolumes[node] = append(v.NodeSpecificVolumes[node], volume)
|
|
}
|
|
|
|
func (v *Volumes) addGroupSpecificVolume(group, volume string) {
|
|
if _, ok := v.GroupSpecificVolumes[group]; !ok {
|
|
v.GroupSpecificVolumes[group] = []string{}
|
|
}
|
|
v.GroupSpecificVolumes[group] = append(v.GroupSpecificVolumes[group], volume)
|
|
}
|