mirror of
https://github.com/siderolabs/talos.git
synced 2025-09-11 17:01:20 +02:00
Containerd doesn't support merging plugin configuration from multiple sources, and Talos has several pieces which configure CRI plugin: (see https://github.com/containerd/containerd/issues/5837) * base config * registry mirror config * system extensions * ... So we implement our own simple way of merging config parts (by simply concatenating text files) to build a final `cri.toml`. At the same time containerd migrated to a new format to specify registry mirror configuration, while old way (via CRI config) is going to be removed in 1.7.0. New way also allows to apply most of registry configuration (except for auth) on the fly. Also, containerd was updated to 1.6.0-rc.0 and runc to 1.1.0. Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
219 lines
5.3 KiB
Go
219 lines
5.3 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 containerd
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/containerd/containerd/remotes/docker"
|
|
"github.com/pelletier/go-toml"
|
|
|
|
"github.com/talos-systems/talos/pkg/machinery/config"
|
|
)
|
|
|
|
// HostsConfig describes layout of registry configuration in "hosts" format.
|
|
//
|
|
// See: https://github.com/containerd/containerd/blob/main/docs/hosts.md
|
|
type HostsConfig struct {
|
|
Directories map[string]*HostsDirectory
|
|
}
|
|
|
|
// HostsDirectory describes a single directory for a specific registry.
|
|
type HostsDirectory struct {
|
|
Files []*HostsFile
|
|
}
|
|
|
|
// HostsFile describes a single file configuring registry.
|
|
//
|
|
// This might be `hosts.toml` or a specific certificate.
|
|
type HostsFile struct {
|
|
Name string
|
|
Contents []byte
|
|
Mode os.FileMode
|
|
}
|
|
|
|
// GenerateHosts generates a structure describing contents of the containerd hosts configuration.
|
|
//
|
|
//nolint:gocyclo
|
|
func GenerateHosts(cfg config.Registries, basePath string) (*HostsConfig, error) {
|
|
config := &HostsConfig{
|
|
Directories: map[string]*HostsDirectory{},
|
|
}
|
|
|
|
configureTLS := func(host string, directoryName string, hostToml *HostToml, directory *HostsDirectory) {
|
|
tlsConfig, ok := cfg.Config()[host]
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
if tlsConfig.TLS().InsecureSkipVerify() {
|
|
hostToml.SkipVerify = true
|
|
}
|
|
|
|
if tlsConfig.TLS().CA() != nil {
|
|
relPath := fmt.Sprintf("%s-ca.crt", host)
|
|
|
|
directory.Files = append(directory.Files,
|
|
&HostsFile{
|
|
Name: relPath,
|
|
Contents: tlsConfig.TLS().CA(),
|
|
Mode: 0o600,
|
|
},
|
|
)
|
|
|
|
hostToml.CACert = filepath.Join(basePath, directoryName, relPath)
|
|
}
|
|
|
|
if tlsConfig.TLS().ClientIdentity() != nil {
|
|
relPathCrt := fmt.Sprintf("%s-client.crt", host)
|
|
relPathKey := fmt.Sprintf("%s-client.key", host)
|
|
|
|
directory.Files = append(directory.Files,
|
|
&HostsFile{
|
|
Name: relPathCrt,
|
|
Contents: tlsConfig.TLS().ClientIdentity().Crt,
|
|
Mode: 0o600,
|
|
},
|
|
&HostsFile{
|
|
Name: relPathKey,
|
|
Contents: tlsConfig.TLS().ClientIdentity().Key,
|
|
Mode: 0o600,
|
|
},
|
|
)
|
|
|
|
hostToml.Client = [][2]string{
|
|
{
|
|
filepath.Join(basePath, directoryName, relPathCrt),
|
|
filepath.Join(basePath, directoryName, relPathKey),
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
// process mirrors
|
|
for registryName, endpoints := range cfg.Mirrors() {
|
|
directoryName := hostDirectory(registryName)
|
|
|
|
directory := &HostsDirectory{}
|
|
|
|
// toml marshaling doesn't guarantee proper order of map keys, so instead we should marshal
|
|
// each time and append to the output
|
|
|
|
var buf bytes.Buffer
|
|
|
|
enc := toml.NewEncoder(&buf)
|
|
|
|
for _, endpoint := range endpoints.Endpoints() {
|
|
hostsToml := HostsToml{
|
|
HostConfigs: map[string]*HostToml{},
|
|
}
|
|
|
|
u, err := url.Parse(endpoint)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error parsing endpoint %q for host %q: %w", endpoint, registryName, err)
|
|
}
|
|
|
|
hostsToml.HostConfigs[endpoint] = &HostToml{
|
|
Capabilities: []string{"pull", "resolve"}, // TODO: we should make it configurable eventually
|
|
}
|
|
|
|
configureTLS(u.Host, directoryName, hostsToml.HostConfigs[endpoint], directory)
|
|
|
|
if err = enc.Encode(hostsToml); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
directory.Files = append(directory.Files,
|
|
&HostsFile{
|
|
Name: "hosts.toml",
|
|
Mode: 0o600,
|
|
Contents: buf.Bytes(),
|
|
},
|
|
)
|
|
|
|
config.Directories[directoryName] = directory
|
|
}
|
|
|
|
// process TLS config for non-mirrored endpoints (even if they were already processed)
|
|
for hostname, tlsConfig := range cfg.Config() {
|
|
directoryName := hostDirectory(hostname)
|
|
|
|
if _, ok := config.Directories[directoryName]; ok {
|
|
// skip, already configured
|
|
continue
|
|
}
|
|
|
|
if tlsConfig.TLS().CA() == nil && tlsConfig.TLS().ClientIdentity() == nil && !tlsConfig.TLS().InsecureSkipVerify() {
|
|
// skip, no specific config
|
|
continue
|
|
}
|
|
|
|
directory := &HostsDirectory{}
|
|
|
|
defaultHost, err := docker.DefaultHost(hostname)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defaultHost = "https://" + defaultHost
|
|
|
|
hostsToml := HostsToml{
|
|
Server: defaultHost,
|
|
HostConfigs: map[string]*HostToml{
|
|
defaultHost: {},
|
|
},
|
|
}
|
|
|
|
configureTLS(hostname, directoryName, hostsToml.HostConfigs[defaultHost], directory)
|
|
|
|
marshaled, err := toml.Marshal(hostsToml)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
directory.Files = append(directory.Files,
|
|
&HostsFile{
|
|
Name: "hosts.toml",
|
|
Mode: 0o600,
|
|
Contents: marshaled,
|
|
},
|
|
)
|
|
|
|
config.Directories[directoryName] = directory
|
|
}
|
|
|
|
return config, nil
|
|
}
|
|
|
|
// hostDirectory converts ":port" to "_port_" in directory names.
|
|
func hostDirectory(host string) string {
|
|
idx := strings.LastIndex(host, ":")
|
|
if idx > 0 {
|
|
return host[:idx] + "_" + host[idx+1:] + "_"
|
|
}
|
|
|
|
return host
|
|
}
|
|
|
|
// HostsToml describes the contents of the `hosts.toml` file.
|
|
type HostsToml struct {
|
|
Server string `toml:"server,omitempty"`
|
|
HostConfigs map[string]*HostToml `toml:"host"`
|
|
}
|
|
|
|
// HostToml is a single entry in `hosts.toml`.
|
|
type HostToml struct {
|
|
Capabilities []string `toml:"capabilities,omitempty"`
|
|
CACert string `toml:"ca,omitempty"`
|
|
Client [][2]string `toml:"client,omitempty"`
|
|
SkipVerify bool `toml:"skip_verify,omitempty"`
|
|
}
|