mirror of
https://github.com/siderolabs/talos.git
synced 2025-08-21 06:31:11 +02:00
This PR will allow users to issue `osctl config generate`, tweak the configs to their liking, then use those configs to call `osctl cluster create`. Example workflow: ``` osctl config generate my-cluster https://10.5.0.2:6443 -o ./my-cluster ** tweaky tweak ** osctl cluster create --name my-cluster --input-dir "$PWD/my-cluster" ``` Signed-off-by: Spencer Smith <robertspencersmith@gmail.com>
210 lines
5.4 KiB
Go
210 lines
5.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"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
|
|
"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/internal/pkg/provision"
|
|
"github.com/talos-systems/talos/pkg/config/types/v1alpha1/generate"
|
|
"github.com/talos-systems/talos/pkg/constants"
|
|
)
|
|
|
|
func (p *provisioner) createNodes(ctx context.Context, clusterReq provision.ClusterRequest, nodeReqs []provision.NodeRequest) ([]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)
|
|
errCh <- err
|
|
|
|
if err == nil {
|
|
nodeCh <- nodeInfo
|
|
}
|
|
}(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) (provision.NodeInfo, error) {
|
|
// Create the container config.
|
|
containerConfig := &container.Config{
|
|
Hostname: nodeReq.Name,
|
|
Image: clusterReq.Image,
|
|
Env: []string{"PLATFORM=container", "USERDATA=" + nodeReq.ConfigData},
|
|
Labels: map[string]string{
|
|
"talos.owned": "true",
|
|
"talos.cluster.name": clusterReq.Name,
|
|
"talos.type": nodeReq.Type.String(),
|
|
},
|
|
Volumes: map[string]struct{}{
|
|
"/var/lib/containerd": {},
|
|
"/var/lib/kubelet": {},
|
|
"/etc/cni": {},
|
|
"/run": {},
|
|
},
|
|
}
|
|
|
|
// 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.
|
|
|
|
switch nodeReq.Type {
|
|
case generate.TypeInit:
|
|
var apidPort nat.Port
|
|
|
|
apidPort, err := nat.NewPort("tcp", "50000")
|
|
if err != nil {
|
|
return provision.NodeInfo{}, err
|
|
}
|
|
|
|
var apiServerPort nat.Port
|
|
|
|
apiServerPort, err = nat.NewPort("tcp", "6443")
|
|
if err != nil {
|
|
return provision.NodeInfo{}, err
|
|
}
|
|
|
|
containerConfig.ExposedPorts = nat.PortSet{
|
|
apidPort: struct{}{},
|
|
apiServerPort: struct{}{},
|
|
}
|
|
|
|
hostConfig.PortBindings = nat.PortMap{
|
|
apidPort: []nat.PortBinding{
|
|
{
|
|
HostIP: "0.0.0.0",
|
|
HostPort: "50000",
|
|
},
|
|
},
|
|
apiServerPort: []nat.PortBinding{
|
|
{
|
|
HostIP: "0.0.0.0",
|
|
HostPort: "6443",
|
|
},
|
|
},
|
|
}
|
|
|
|
fallthrough
|
|
case generate.TypeControlPlane:
|
|
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.Type,
|
|
|
|
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()
|
|
}
|