mirror of
https://github.com/siderolabs/talos.git
synced 2025-08-21 14:41:12 +02:00
This change is only moving packages and updating import paths. Goal: expose `internal/pkg/provision` as `pkg/provision` to enable other projects to import Talos provisioning library. As cluster checks are almost always required as part of provisioning process, package `internal/pkg/cluster` was also made public as `pkg/cluster`. Other changes were direct dependencies discovered by `importvet` which were updated. Public packages (useful, general purpose packages with stable API): * `internal/pkg/conditions` -> `pkg/conditions` * `internal/pkg/tail` -> `pkg/tail` Private packages (used only on provisioning library internally): * `internal/pkg/inmemhttp` -> `pkg/provision/internal/inmemhttp` * `internal/pkg/kernel/vmlinuz` -> `pkg/provision/internal/vmlinuz` * `internal/pkg/cniutils` -> `pkg/provision/internal/cniutils` Signed-off-by: Andrey Smirnov <smirnov.andrey@gmail.com>
240 lines
6.4 KiB
Go
240 lines
6.4 KiB
Go
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
package docker
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"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/network"
|
|
"github.com/docker/go-connections/nat"
|
|
"github.com/hashicorp/go-multierror"
|
|
|
|
"github.com/talos-systems/talos/pkg/config/types/v1alpha1/machine"
|
|
"github.com/talos-systems/talos/pkg/constants"
|
|
"github.com/talos-systems/talos/pkg/provision"
|
|
)
|
|
|
|
type portMap struct {
|
|
exposedPorts nat.PortSet
|
|
portBindings nat.PortMap
|
|
}
|
|
|
|
func (p *provisioner) createNodes(ctx context.Context, clusterReq provision.ClusterRequest, nodeReqs []provision.NodeRequest, options *provision.Options) ([]provision.NodeInfo, error) {
|
|
errCh := make(chan error)
|
|
nodeCh := make(chan provision.NodeInfo, len(nodeReqs))
|
|
|
|
for _, nodeReq := range nodeReqs {
|
|
go func(nodeReq provision.NodeRequest) {
|
|
nodeInfo, err := p.createNode(ctx, clusterReq, nodeReq, options)
|
|
if err == nil {
|
|
nodeCh <- nodeInfo
|
|
}
|
|
|
|
errCh <- err
|
|
}(nodeReq)
|
|
}
|
|
|
|
var multiErr *multierror.Error
|
|
|
|
for range nodeReqs {
|
|
multiErr = multierror.Append(multiErr, <-errCh)
|
|
}
|
|
|
|
close(nodeCh)
|
|
|
|
nodesInfo := make([]provision.NodeInfo, 0, len(nodeReqs))
|
|
|
|
for nodeInfo := range nodeCh {
|
|
nodesInfo = append(nodesInfo, nodeInfo)
|
|
}
|
|
|
|
return nodesInfo, multiErr.ErrorOrNil()
|
|
}
|
|
|
|
//nolint: gocyclo
|
|
func (p *provisioner) createNode(ctx context.Context, clusterReq provision.ClusterRequest, nodeReq provision.NodeRequest, options *provision.Options) (provision.NodeInfo, error) {
|
|
cfg, err := nodeReq.Config.String()
|
|
if err != nil {
|
|
return provision.NodeInfo{}, err
|
|
}
|
|
|
|
// Create the container config.
|
|
containerConfig := &container.Config{
|
|
Hostname: nodeReq.Name,
|
|
Image: clusterReq.Image,
|
|
Env: []string{"PLATFORM=container", "USERDATA=" + base64.StdEncoding.EncodeToString([]byte(cfg))},
|
|
Labels: map[string]string{
|
|
"talos.owned": "true",
|
|
"talos.cluster.name": clusterReq.Name,
|
|
"talos.type": nodeReq.Config.Machine().Type().String(),
|
|
},
|
|
Volumes: map[string]struct{}{
|
|
"/var/lib/containerd": {},
|
|
"/var/lib/kubelet": {},
|
|
"/etc/cni": {},
|
|
"/run": {},
|
|
"/system": {},
|
|
},
|
|
}
|
|
|
|
// Create the host config.
|
|
|
|
hostConfig := &container.HostConfig{
|
|
Privileged: true,
|
|
SecurityOpt: []string{"seccomp:unconfined"},
|
|
Resources: container.Resources{
|
|
NanoCPUs: nodeReq.NanoCPUs,
|
|
Memory: nodeReq.Memory,
|
|
},
|
|
}
|
|
|
|
// Ensure that the container is created in the talos network.
|
|
|
|
networkConfig := &network.NetworkingConfig{
|
|
EndpointsConfig: map[string]*network.EndpointSettings{
|
|
clusterReq.Network.Name: {
|
|
NetworkID: clusterReq.Network.Name,
|
|
},
|
|
},
|
|
}
|
|
|
|
// Mutate the container configurations based on the node type.
|
|
|
|
if nodeReq.Config.Machine().Type() == machine.TypeInit || nodeReq.Config.Machine().Type() == machine.TypeControlPlane {
|
|
portsToOpen := nodeReq.Ports
|
|
|
|
if len(options.DockerPorts) > 0 {
|
|
portsToOpen = append(portsToOpen, options.DockerPorts...)
|
|
}
|
|
|
|
var generatedPortMap portMap
|
|
|
|
generatedPortMap, err = genPortMap(portsToOpen, options.DockerPortsHostIP)
|
|
if err != nil {
|
|
return provision.NodeInfo{}, err
|
|
}
|
|
|
|
containerConfig.ExposedPorts = generatedPortMap.exposedPorts
|
|
|
|
hostConfig.PortBindings = generatedPortMap.portBindings
|
|
|
|
containerConfig.Volumes[constants.EtcdDataPath] = struct{}{}
|
|
|
|
if nodeReq.IP == nil {
|
|
return provision.NodeInfo{}, errors.New("an IP address must be provided when creating a master node")
|
|
}
|
|
}
|
|
|
|
if nodeReq.IP != nil {
|
|
networkConfig.EndpointsConfig[clusterReq.Network.Name].IPAMConfig = &network.EndpointIPAMConfig{IPv4Address: nodeReq.IP.String()}
|
|
}
|
|
|
|
// Create the container.
|
|
resp, err := p.client.ContainerCreate(ctx, containerConfig, hostConfig, networkConfig, nodeReq.Name)
|
|
if err != nil {
|
|
return provision.NodeInfo{}, err
|
|
}
|
|
|
|
// Start the container.
|
|
err = p.client.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{})
|
|
if err != nil {
|
|
return provision.NodeInfo{}, err
|
|
}
|
|
|
|
// Inspect the container.
|
|
info, err := p.client.ContainerInspect(ctx, resp.ID)
|
|
if err != nil {
|
|
return provision.NodeInfo{}, err
|
|
}
|
|
|
|
nodeInfo := provision.NodeInfo{
|
|
ID: info.ID,
|
|
Name: info.Name,
|
|
Type: nodeReq.Config.Machine().Type(),
|
|
|
|
NanoCPUs: nodeReq.NanoCPUs,
|
|
Memory: nodeReq.Memory,
|
|
|
|
PrivateIP: net.ParseIP(info.NetworkSettings.Networks[clusterReq.Network.Name].IPAddress),
|
|
}
|
|
|
|
return nodeInfo, nil
|
|
}
|
|
|
|
func (p *provisioner) listNodes(ctx context.Context, clusterName string) ([]types.Container, error) {
|
|
filters := filters.NewArgs()
|
|
filters.Add("label", "talos.owned=true")
|
|
filters.Add("label", "talos.cluster.name="+clusterName)
|
|
|
|
return p.client.ContainerList(ctx, types.ContainerListOptions{All: true, Filters: filters})
|
|
}
|
|
|
|
func (p *provisioner) destroyNodes(ctx context.Context, clusterName string, options *provision.Options) error {
|
|
containers, err := p.listNodes(ctx, clusterName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
errCh := make(chan error)
|
|
|
|
for _, container := range containers {
|
|
go func(container types.Container) {
|
|
fmt.Fprintln(options.LogWriter, "destroying node", container.Names[0][1:])
|
|
|
|
errCh <- p.client.ContainerRemove(ctx, container.ID, types.ContainerRemoveOptions{RemoveVolumes: true, Force: true})
|
|
}(container)
|
|
}
|
|
|
|
var multiErr *multierror.Error
|
|
|
|
for range containers {
|
|
multiErr = multierror.Append(multiErr, <-errCh)
|
|
}
|
|
|
|
return multiErr.ErrorOrNil()
|
|
}
|
|
|
|
func genPortMap(portList []string, hostIP string) (portMap, error) {
|
|
portSetRet := nat.PortSet{}
|
|
portMapRet := nat.PortMap{}
|
|
|
|
for _, port := range portList {
|
|
explodedPortAndProtocol := strings.Split(port, "/")
|
|
|
|
if len(explodedPortAndProtocol) != 2 {
|
|
return portMap{}, errors.New("incorrect format for exposed port/protocols")
|
|
}
|
|
|
|
explodedPort := strings.Split(explodedPortAndProtocol[0], ":")
|
|
|
|
if len(explodedPort) != 2 {
|
|
return portMap{}, errors.New("incorrect format for exposed ports")
|
|
}
|
|
|
|
natPort, err := nat.NewPort(explodedPortAndProtocol[1], explodedPort[1])
|
|
if err != nil {
|
|
return portMap{}, err
|
|
}
|
|
|
|
portSetRet[natPort] = struct{}{}
|
|
portMapRet[natPort] = []nat.PortBinding{
|
|
{
|
|
HostIP: hostIP,
|
|
HostPort: explodedPort[0],
|
|
},
|
|
}
|
|
}
|
|
|
|
return portMap{portSetRet, portMapRet}, nil
|
|
}
|